diff options
-rw-r--r-- | bitbake/lib/bb/ui/crumbs/configurator.py | 278 | ||||
-rw-r--r-- | bitbake/lib/bb/ui/crumbs/hig.py | 61 | ||||
-rw-r--r-- | bitbake/lib/bb/ui/crumbs/hobeventhandler.py | 218 | ||||
-rw-r--r-- | bitbake/lib/bb/ui/crumbs/hobprefs.py | 293 | ||||
-rw-r--r-- | bitbake/lib/bb/ui/crumbs/layereditor.py | 136 | ||||
-rw-r--r-- | bitbake/lib/bb/ui/crumbs/runningbuild.py | 12 | ||||
-rw-r--r-- | bitbake/lib/bb/ui/crumbs/tasklistmodel.py | 326 | ||||
-rw-r--r-- | bitbake/lib/bb/ui/hob.py | 924 |
8 files changed, 1824 insertions, 424 deletions
diff --git a/bitbake/lib/bb/ui/crumbs/configurator.py b/bitbake/lib/bb/ui/crumbs/configurator.py new file mode 100644 index 0000000000..b694143d4c --- /dev/null +++ b/bitbake/lib/bb/ui/crumbs/configurator.py | |||
@@ -0,0 +1,278 @@ | |||
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 copy | ||
23 | import re, os | ||
24 | from bb import data | ||
25 | |||
26 | class Configurator(gobject.GObject): | ||
27 | |||
28 | """ | ||
29 | A GObject to handle writing modified configuration values back | ||
30 | to conf files. | ||
31 | """ | ||
32 | __gsignals__ = { | ||
33 | "layers-loaded" : (gobject.SIGNAL_RUN_LAST, | ||
34 | gobject.TYPE_NONE, | ||
35 | ()), | ||
36 | "layers-changed" : (gobject.SIGNAL_RUN_LAST, | ||
37 | gobject.TYPE_NONE, | ||
38 | ()) | ||
39 | } | ||
40 | |||
41 | def __init__(self): | ||
42 | gobject.GObject.__init__(self) | ||
43 | self.local = None | ||
44 | self.bblayers = None | ||
45 | self.enabled_layers = {} | ||
46 | self.loaded_layers = {} | ||
47 | self.config = {} | ||
48 | self.orig_config = {} | ||
49 | |||
50 | # NOTE: cribbed from the cooker... | ||
51 | def _parse(self, f, data, include=False): | ||
52 | try: | ||
53 | return bb.parse.handle(f, data, include) | ||
54 | except (IOError, bb.parse.ParseError) as exc: | ||
55 | parselog.critical("Unable to parse %s: %s" % (f, exc)) | ||
56 | sys.exit(1) | ||
57 | |||
58 | def _loadLocalConf(self, path): | ||
59 | def getString(var): | ||
60 | return bb.data.getVar(var, data, True) or "" | ||
61 | |||
62 | self.local = path | ||
63 | |||
64 | if self.orig_config: | ||
65 | del self.orig_config | ||
66 | self.orig_config = {} | ||
67 | |||
68 | data = bb.data.init() | ||
69 | data = self._parse(self.local, data) | ||
70 | |||
71 | # We only need to care about certain variables | ||
72 | mach = getString('MACHINE') | ||
73 | if mach and mach != self.config.get('MACHINE', ''): | ||
74 | self.config['MACHINE'] = mach | ||
75 | sdkmach = getString('SDKMACHINE') | ||
76 | if sdkmach and sdkmach != self.config.get('SDKMACHINE', ''): | ||
77 | self.config['SDKMACHINE'] = sdkmach | ||
78 | distro = getString('DISTRO') | ||
79 | if distro and distro != self.config.get('DISTRO', ''): | ||
80 | self.config['DISTRO'] = distro | ||
81 | bbnum = getString('BB_NUMBER_THREADS') | ||
82 | if bbnum and bbnum != self.config.get('BB_NUMBER_THREADS', ''): | ||
83 | self.config['BB_NUMBER_THREADS'] = bbnum | ||
84 | pmake = getString('PARALLEL_MAKE') | ||
85 | if pmake and pmake != self.config.get('PARALLEL_MAKE', ''): | ||
86 | self.config['PARALLEL_MAKE'] = pmake | ||
87 | incompat = getString('INCOMPATIBLE_LICENSE') | ||
88 | if incompat and incompat != self.config.get('INCOMPATIBLE_LICENSE', ''): | ||
89 | self.config['INCOMPATIBLE_LICENSE'] = incompat | ||
90 | pclass = getString('PACKAGE_CLASSES') | ||
91 | if pclass and pclass != self.config.get('PACKAGE_CLASSES', ''): | ||
92 | self.config['PACKAGE_CLASSES'] = pclass | ||
93 | |||
94 | self.orig_config = copy.deepcopy(self.config) | ||
95 | |||
96 | def setLocalConfVar(self, var, val): | ||
97 | if var in self.config: | ||
98 | self.config[var] = val | ||
99 | |||
100 | def _loadLayerConf(self, path): | ||
101 | self.bblayers = path | ||
102 | self.enabled_layers = {} | ||
103 | self.loaded_layers = {} | ||
104 | data = bb.data.init() | ||
105 | data = self._parse(self.bblayers, data) | ||
106 | layers = (bb.data.getVar('BBLAYERS', data, True) or "").split() | ||
107 | for layer in layers: | ||
108 | # TODO: we may be better off calling the layer by its | ||
109 | # BBFILE_COLLECTIONS value? | ||
110 | name = self._getLayerName(layer) | ||
111 | self.loaded_layers[name] = layer | ||
112 | |||
113 | self.enabled_layers = copy.deepcopy(self.loaded_layers) | ||
114 | self.emit("layers-loaded") | ||
115 | |||
116 | def _addConfigFile(self, path): | ||
117 | pref, sep, filename = path.rpartition("/") | ||
118 | if filename == "local.conf" or filename == "hob.local.conf": | ||
119 | self._loadLocalConf(path) | ||
120 | elif filename == "bblayers.conf": | ||
121 | self._loadLayerConf(path) | ||
122 | |||
123 | def _splitLayer(self, path): | ||
124 | # we only care about the path up to /conf/layer.conf | ||
125 | layerpath, conf, end = path.rpartition("/conf/") | ||
126 | return layerpath | ||
127 | |||
128 | def _getLayerName(self, path): | ||
129 | # Should this be the collection name? | ||
130 | layerpath, sep, name = path.rpartition("/") | ||
131 | return name | ||
132 | |||
133 | def disableLayer(self, layer): | ||
134 | if layer in self.enabled_layers: | ||
135 | del self.enabled_layers[layer] | ||
136 | |||
137 | def addLayerConf(self, confpath): | ||
138 | layerpath = self._splitLayer(confpath) | ||
139 | name = self._getLayerName(layerpath) | ||
140 | if name not in self.enabled_layers: | ||
141 | self.addLayer(name, layerpath) | ||
142 | return name, layerpath | ||
143 | |||
144 | def addLayer(self, name, path): | ||
145 | self.enabled_layers[name] = path | ||
146 | |||
147 | def _isLayerConfDirty(self): | ||
148 | # if a different number of layers enabled to what was | ||
149 | # loaded, definitely different | ||
150 | if len(self.enabled_layers) != len(self.loaded_layers): | ||
151 | return True | ||
152 | |||
153 | for layer in self.loaded_layers: | ||
154 | # if layer loaded but no longer present, definitely dirty | ||
155 | if layer not in self.enabled_layers: | ||
156 | return True | ||
157 | |||
158 | for layer in self.enabled_layers: | ||
159 | # if this layer wasn't present at load, definitely dirty | ||
160 | if layer not in self.loaded_layers: | ||
161 | return True | ||
162 | # if this layers path has changed, definitely dirty | ||
163 | if self.enabled_layers[layer] != self.loaded_layers[layer]: | ||
164 | return True | ||
165 | |||
166 | return False | ||
167 | |||
168 | def _constructLayerEntry(self): | ||
169 | """ | ||
170 | Returns a string representing the new layer selection | ||
171 | """ | ||
172 | layers = self.enabled_layers.copy() | ||
173 | # Construct BBLAYERS entry | ||
174 | layer_entry = "BBLAYERS = \" \\\n" | ||
175 | if 'meta' in layers: | ||
176 | layer_entry = layer_entry + " %s \\\n" % layers['meta'] | ||
177 | del layers['meta'] | ||
178 | for layer in layers: | ||
179 | layer_entry = layer_entry + " %s \\\n" % layers[layer] | ||
180 | layer_entry = layer_entry + " \"" | ||
181 | |||
182 | return "".join(layer_entry) | ||
183 | |||
184 | def writeLocalConf(self): | ||
185 | # Dictionary containing only new or modified variables | ||
186 | changed_values = {} | ||
187 | for var in self.config: | ||
188 | val = self.config[var] | ||
189 | if self.orig_config.get(var, None) != val: | ||
190 | changed_values[var] = val | ||
191 | |||
192 | if not len(changed_values): | ||
193 | return | ||
194 | |||
195 | # Create a backup of the local.conf | ||
196 | bkup = "%s~" % self.local | ||
197 | os.rename(self.local, bkup) | ||
198 | |||
199 | # read the original conf into a list | ||
200 | with open(bkup, 'r') as config: | ||
201 | config_lines = config.readlines() | ||
202 | |||
203 | new_config_lines = ["\n"] | ||
204 | for var in changed_values: | ||
205 | # Convenience function for re.subn(). If the pattern matches | ||
206 | # return a string which contains an assignment using the same | ||
207 | # assignment operator as the old assignment. | ||
208 | def replace_val(matchobj): | ||
209 | var = matchobj.group(1) # config variable | ||
210 | op = matchobj.group(2) # assignment operator | ||
211 | val = changed_values[var] # new config value | ||
212 | return "%s %s \"%s\"" % (var, op, val) | ||
213 | |||
214 | pattern = '^\s*(%s)\s*([+=?.]+)(.*)' % re.escape(var) | ||
215 | p = re.compile(pattern) | ||
216 | cnt = 0 | ||
217 | replaced = False | ||
218 | |||
219 | # Iterate over the local.conf lines and if they are a match | ||
220 | # for the pattern comment out the line and append a new line | ||
221 | # with the new VAR op "value" entry | ||
222 | for line in config_lines: | ||
223 | new_line, replacements = p.subn(replace_val, line) | ||
224 | if replacements: | ||
225 | config_lines[cnt] = "#%s" % line | ||
226 | new_config_lines.append(new_line) | ||
227 | replaced = True | ||
228 | cnt = cnt + 1 | ||
229 | |||
230 | if not replaced: | ||
231 | new_config_lines.append("%s = \"%s\"" % (var, changed_values[var])) | ||
232 | |||
233 | # Add the modified variables | ||
234 | config_lines.extend(new_config_lines) | ||
235 | |||
236 | # Write the updated lines list object to the local.conf | ||
237 | with open(self.local, "w") as n: | ||
238 | n.write("".join(config_lines)) | ||
239 | |||
240 | del self.orig_config | ||
241 | self.orig_config = copy.deepcopy(self.config) | ||
242 | |||
243 | def writeLayerConf(self): | ||
244 | # If we've not added/removed new layers don't write | ||
245 | if not self._isLayerConfDirty(): | ||
246 | return | ||
247 | |||
248 | # This pattern should find the existing BBLAYERS | ||
249 | pattern = 'BBLAYERS\s=\s\".*\"' | ||
250 | |||
251 | # Backup the users bblayers.conf | ||
252 | bkup = "%s~" % self.bblayers | ||
253 | os.rename(self.bblayers, bkup) | ||
254 | |||
255 | replacement = self._constructLayerEntry() | ||
256 | |||
257 | with open(bkup, "r") as f: | ||
258 | contents = f.read() | ||
259 | p = re.compile(pattern, re.DOTALL) | ||
260 | new = p.sub(replacement, contents) | ||
261 | |||
262 | with open(self.bblayers, "w") as n: | ||
263 | n.write(new) | ||
264 | |||
265 | # At some stage we should remove the backup we've created | ||
266 | # though we should probably verify it first | ||
267 | #os.remove(bkup) | ||
268 | |||
269 | # set loaded_layers for dirtiness tracking | ||
270 | self.loaded_layers = copy.deepcopy(self.enabled_layers) | ||
271 | |||
272 | self.emit("layers-changed") | ||
273 | |||
274 | def configFound(self, handler, path): | ||
275 | self._addConfigFile(path) | ||
276 | |||
277 | def loadConfig(self, path): | ||
278 | self._addConfigFile(path) | ||
diff --git a/bitbake/lib/bb/ui/crumbs/hig.py b/bitbake/lib/bb/ui/crumbs/hig.py new file mode 100644 index 0000000000..b3b3c7a72e --- /dev/null +++ b/bitbake/lib/bb/ui/crumbs/hig.py | |||
@@ -0,0 +1,61 @@ | |||
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 | """ | ||
24 | The following are convenience classes for implementing GNOME HIG compliant | ||
25 | BitBake GUI's | ||
26 | In summary: spacing = 12px, border-width = 6px | ||
27 | """ | ||
28 | |||
29 | class CrumbsDialog(gtk.Dialog): | ||
30 | """ | ||
31 | A GNOME HIG compliant dialog widget. | ||
32 | Add buttons with gtk.Dialog.add_button or gtk.Dialog.add_buttons | ||
33 | """ | ||
34 | def __init__(self, parent=None, label="", icon=gtk.STOCK_INFO): | ||
35 | gtk.Dialog.__init__(self, "", parent, gtk.DIALOG_DESTROY_WITH_PARENT) | ||
36 | |||
37 | #self.set_property("has-separator", False) # note: deprecated in 2.22 | ||
38 | |||
39 | self.set_border_width(6) | ||
40 | self.vbox.set_property("spacing", 12) | ||
41 | self.action_area.set_property("spacing", 12) | ||
42 | self.action_area.set_property("border-width", 6) | ||
43 | |||
44 | first_row = gtk.HBox(spacing=12) | ||
45 | first_row.set_property("border-width", 6) | ||
46 | first_row.show() | ||
47 | self.vbox.add(first_row) | ||
48 | |||
49 | self.icon = gtk.Image() | ||
50 | self.icon.set_from_stock(icon, gtk.ICON_SIZE_DIALOG) | ||
51 | self.icon.set_property("yalign", 0.00) | ||
52 | self.icon.show() | ||
53 | first_row.add(self.icon) | ||
54 | |||
55 | self.label = gtk.Label() | ||
56 | self.label.set_use_markup(True) | ||
57 | self.label.set_line_wrap(True) | ||
58 | self.label.set_markup(label) | ||
59 | self.label.set_property("yalign", 0.00) | ||
60 | self.label.show() | ||
61 | first_row.add(self.label) | ||
diff --git a/bitbake/lib/bb/ui/crumbs/hobeventhandler.py b/bitbake/lib/bb/ui/crumbs/hobeventhandler.py index c474491d6a..fa79e0c7a2 100644 --- a/bitbake/lib/bb/ui/crumbs/hobeventhandler.py +++ b/bitbake/lib/bb/ui/crumbs/hobeventhandler.py | |||
@@ -19,7 +19,6 @@ | |||
19 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | 19 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
20 | 20 | ||
21 | import gobject | 21 | import gobject |
22 | from bb.ui.crumbs.progress import ProgressBar | ||
23 | 22 | ||
24 | progress_total = 0 | 23 | progress_total = 0 |
25 | 24 | ||
@@ -29,46 +28,78 @@ class HobHandler(gobject.GObject): | |||
29 | This object does BitBake event handling for the hob gui. | 28 | This object does BitBake event handling for the hob gui. |
30 | """ | 29 | """ |
31 | __gsignals__ = { | 30 | __gsignals__ = { |
32 | "machines-updated" : (gobject.SIGNAL_RUN_LAST, | 31 | "machines-updated" : (gobject.SIGNAL_RUN_LAST, |
33 | gobject.TYPE_NONE, | 32 | gobject.TYPE_NONE, |
34 | (gobject.TYPE_PYOBJECT,)), | 33 | (gobject.TYPE_PYOBJECT,)), |
35 | "distros-updated" : (gobject.SIGNAL_RUN_LAST, | 34 | "sdk-machines-updated": (gobject.SIGNAL_RUN_LAST, |
36 | gobject.TYPE_NONE, | 35 | gobject.TYPE_NONE, |
37 | (gobject.TYPE_PYOBJECT,)), | 36 | (gobject.TYPE_PYOBJECT,)), |
38 | "generating-data" : (gobject.SIGNAL_RUN_LAST, | 37 | "distros-updated" : (gobject.SIGNAL_RUN_LAST, |
39 | gobject.TYPE_NONE, | 38 | gobject.TYPE_NONE, |
40 | ()), | 39 | (gobject.TYPE_PYOBJECT,)), |
41 | "data-generated" : (gobject.SIGNAL_RUN_LAST, | 40 | "package-formats-found" : (gobject.SIGNAL_RUN_LAST, |
42 | gobject.TYPE_NONE, | 41 | gobject.TYPE_NONE, |
43 | ()) | 42 | (gobject.TYPE_PYOBJECT,)), |
43 | "config-found" : (gobject.SIGNAL_RUN_LAST, | ||
44 | gobject.TYPE_NONE, | ||
45 | (gobject.TYPE_STRING,)), | ||
46 | "generating-data" : (gobject.SIGNAL_RUN_LAST, | ||
47 | gobject.TYPE_NONE, | ||
48 | ()), | ||
49 | "data-generated" : (gobject.SIGNAL_RUN_LAST, | ||
50 | gobject.TYPE_NONE, | ||
51 | ()), | ||
52 | "error" : (gobject.SIGNAL_RUN_LAST, | ||
53 | gobject.TYPE_NONE, | ||
54 | (gobject.TYPE_STRING,)), | ||
55 | "build-complete" : (gobject.SIGNAL_RUN_LAST, | ||
56 | gobject.TYPE_NONE, | ||
57 | ()), | ||
58 | "reload-triggered" : (gobject.SIGNAL_RUN_LAST, | ||
59 | gobject.TYPE_NONE, | ||
60 | (gobject.TYPE_STRING, | ||
61 | gobject.TYPE_STRING)), | ||
44 | } | 62 | } |
45 | 63 | ||
46 | def __init__(self, taskmodel, server): | 64 | def __init__(self, taskmodel, server): |
47 | gobject.GObject.__init__(self) | 65 | gobject.GObject.__init__(self) |
66 | self.current_command = None | ||
67 | self.building = None | ||
68 | self.gplv3_excluded = False | ||
69 | self.build_toolchain = False | ||
70 | self.build_toolchain_headers = False | ||
71 | self.generating = False | ||
72 | self.build_queue = [] | ||
48 | 73 | ||
49 | self.model = taskmodel | 74 | self.model = taskmodel |
50 | self.server = server | 75 | self.server = server |
51 | self.current_command = None | ||
52 | self.building = False | ||
53 | 76 | ||
54 | self.command_map = { | 77 | self.command_map = { |
55 | "findConfigFilesDistro" : ("findConfigFiles", "MACHINE", "findConfigFilesMachine"), | 78 | "findConfigFilePathLocal" : ("findConfigFilePath", ["hob.local.conf"], "findConfigFilePathHobLocal"), |
56 | "findConfigFilesMachine" : ("generateTargetsTree", "classes/image.bbclass", None), | 79 | "findConfigFilePathHobLocal" : ("findConfigFilePath", ["bblayers.conf"], "findConfigFilePathLayers"), |
57 | "generateTargetsTree" : (None, None, None), | 80 | "findConfigFilePathLayers" : ("findConfigFiles", ["DISTRO"], "findConfigFilesDistro"), |
81 | "findConfigFilesDistro" : ("findConfigFiles", ["MACHINE"], "findConfigFilesMachine"), | ||
82 | "findConfigFilesMachine" : ("findConfigFiles", ["MACHINE-SDK"], "findConfigFilesSdkMachine"), | ||
83 | "findConfigFilesSdkMachine" : ("findFilesMatchingInDir", ["rootfs_", "classes"], "findFilesMatchingPackage"), | ||
84 | "findFilesMatchingPackage" : ("generateTargetsTree", ["classes/image.bbclass"], None), | ||
85 | "generateTargetsTree" : (None, [], None), | ||
58 | } | 86 | } |
59 | 87 | ||
60 | def run_next_command(self): | 88 | def run_next_command(self): |
61 | # FIXME: this is ugly and I *will* replace it | 89 | # FIXME: this is ugly and I *will* replace it |
62 | if self.current_command: | 90 | if self.current_command: |
91 | if not self.generating: | ||
92 | self.emit("generating-data") | ||
93 | self.generating = True | ||
63 | next_cmd = self.command_map[self.current_command] | 94 | next_cmd = self.command_map[self.current_command] |
64 | command = next_cmd[0] | 95 | command = next_cmd[0] |
65 | argument = next_cmd[1] | 96 | argument = next_cmd[1] |
66 | self.current_command = next_cmd[2] | 97 | self.current_command = next_cmd[2] |
67 | if command == "generateTargetsTree": | 98 | args = [command] |
68 | self.emit("generating-data") | 99 | args.extend(argument) |
69 | self.server.runCommand([command, argument]) | 100 | self.server.runCommand(args) |
70 | 101 | ||
71 | def handle_event(self, event, running_build, pbar=None): | 102 | def handle_event(self, event, running_build, pbar): |
72 | if not event: | 103 | if not event: |
73 | return | 104 | return |
74 | 105 | ||
@@ -77,9 +108,9 @@ class HobHandler(gobject.GObject): | |||
77 | running_build.handle_event(event) | 108 | running_build.handle_event(event) |
78 | elif isinstance(event, bb.event.TargetsTreeGenerated): | 109 | elif isinstance(event, bb.event.TargetsTreeGenerated): |
79 | self.emit("data-generated") | 110 | self.emit("data-generated") |
111 | self.generating = False | ||
80 | if event._model: | 112 | if event._model: |
81 | self.model.populate(event._model) | 113 | self.model.populate(event._model) |
82 | |||
83 | elif isinstance(event, bb.event.ConfigFilesFound): | 114 | elif isinstance(event, bb.event.ConfigFilesFound): |
84 | var = event._variable | 115 | var = event._variable |
85 | if var == "distro": | 116 | if var == "distro": |
@@ -90,28 +121,44 @@ class HobHandler(gobject.GObject): | |||
90 | machines = event._values | 121 | machines = event._values |
91 | machines.sort() | 122 | machines.sort() |
92 | self.emit("machines-updated", machines) | 123 | self.emit("machines-updated", machines) |
93 | 124 | elif var == "machine-sdk": | |
125 | sdk_machines = event._values | ||
126 | sdk_machines.sort() | ||
127 | self.emit("sdk-machines-updated", sdk_machines) | ||
128 | elif isinstance(event, bb.event.ConfigFilePathFound): | ||
129 | path = event._path | ||
130 | self.emit("config-found", path) | ||
131 | elif isinstance(event, bb.event.FilesMatchingFound): | ||
132 | # FIXME: hard coding, should at least be a variable shared between | ||
133 | # here and the caller | ||
134 | if event._pattern == "rootfs_": | ||
135 | formats = [] | ||
136 | for match in event._matches: | ||
137 | classname, sep, cls = match.rpartition(".") | ||
138 | fs, sep, format = classname.rpartition("_") | ||
139 | formats.append(format) | ||
140 | formats.sort() | ||
141 | self.emit("package-formats-found", formats) | ||
94 | elif isinstance(event, bb.command.CommandCompleted): | 142 | elif isinstance(event, bb.command.CommandCompleted): |
95 | self.run_next_command() | 143 | self.run_next_command() |
96 | elif isinstance(event, bb.event.CacheLoadStarted) and pbar: | 144 | elif isinstance(event, bb.command.CommandFailed): |
97 | pbar.set_title("Loading cache") | 145 | self.emit("error", event.error) |
146 | elif isinstance(event, bb.event.CacheLoadStarted): | ||
98 | bb.ui.crumbs.hobeventhandler.progress_total = event.total | 147 | bb.ui.crumbs.hobeventhandler.progress_total = event.total |
99 | pbar.update(0, bb.ui.crumbs.hobeventhandler.progress_total) | 148 | pbar.set_text("Loading cache: %s/%s" % (0, bb.ui.crumbs.hobeventhandler.progress_total)) |
100 | elif isinstance(event, bb.event.CacheLoadProgress) and pbar: | 149 | elif isinstance(event, bb.event.CacheLoadProgress): |
101 | pbar.update(event.current, bb.ui.crumbs.hobeventhandler.progress_total) | 150 | pbar.set_text("Loading cache: %s/%s" % (event.current, bb.ui.crumbs.hobeventhandler.progress_total)) |
102 | elif isinstance(event, bb.event.CacheLoadCompleted) and pbar: | 151 | elif isinstance(event, bb.event.CacheLoadCompleted): |
103 | pbar.update(bb.ui.crumbs.hobeventhandler.progress_total, bb.ui.crumbs.hobeventhandler.progress_total) | 152 | pbar.set_text("Loading cache: %s/%s" % (bb.ui.crumbs.hobeventhandler.progress_total, bb.ui.crumbs.hobeventhandler.progress_total)) |
104 | elif isinstance(event, bb.event.ParseStarted) and pbar: | 153 | elif isinstance(event, bb.event.ParseStarted): |
105 | if event.total == 0: | 154 | if event.total == 0: |
106 | return | 155 | return |
107 | pbar.set_title("Processing recipes") | ||
108 | bb.ui.crumbs.hobeventhandler.progress_total = event.total | 156 | bb.ui.crumbs.hobeventhandler.progress_total = event.total |
109 | pbar.update(0, bb.ui.crumbs.hobeventhandler.progress_total) | 157 | pbar.set_text("Processing recipes: %s/%s" % (0, bb.ui.crumbs.hobeventhandler.progress_total)) |
110 | elif isinstance(event, bb.event.ParseProgress) and pbar: | 158 | elif isinstance(event, bb.event.ParseProgress): |
111 | pbar.update(event.current, bb.ui.crumbs.hobeventhandler.progress_total) | 159 | pbar.set_text("Processing recipes: %s/%s" % (event.current, bb.ui.crumbs.hobeventhandler.progress_total)) |
112 | elif isinstance(event, bb.event.ParseCompleted) and pbar: | 160 | elif isinstance(event, bb.event.ParseCompleted): |
113 | pbar.hide() | 161 | pbar.set_fraction(1.0) |
114 | |||
115 | return | 162 | return |
116 | 163 | ||
117 | def event_handle_idle_func (self, eventHandler, running_build, pbar): | 164 | def event_handle_idle_func (self, eventHandler, running_build, pbar): |
@@ -124,16 +171,95 @@ class HobHandler(gobject.GObject): | |||
124 | 171 | ||
125 | def set_machine(self, machine): | 172 | def set_machine(self, machine): |
126 | self.server.runCommand(["setVariable", "MACHINE", machine]) | 173 | self.server.runCommand(["setVariable", "MACHINE", machine]) |
127 | self.current_command = "findConfigFilesMachine" | 174 | |
128 | self.run_next_command() | 175 | def set_sdk_machine(self, sdk_machine): |
176 | self.server.runCommand(["setVariable", "SDKMACHINE", sdk_machine]) | ||
129 | 177 | ||
130 | def set_distro(self, distro): | 178 | def set_distro(self, distro): |
131 | self.server.runCommand(["setVariable", "DISTRO", distro]) | 179 | self.server.runCommand(["setVariable", "DISTRO", distro]) |
132 | 180 | ||
133 | def run_build(self, targets): | 181 | def set_package_format(self, format): |
134 | self.building = True | 182 | self.server.runCommand(["setVariable", "PACKAGE_CLASSES", "package_%s" % format]) |
183 | |||
184 | def reload_data(self, config=None): | ||
185 | img = self.model.selected_image | ||
186 | selected_packages, _ = self.model.get_selected_packages() | ||
187 | self.emit("reload-triggered", img, " ".join(selected_packages)) | ||
188 | self.server.runCommand(["reparseFiles"]) | ||
189 | self.current_command = "findConfigFilePathLayers" | ||
190 | self.run_next_command() | ||
191 | |||
192 | def set_bbthreads(self, threads): | ||
193 | self.server.runCommand(["setVariable", "BB_NUMBER_THREADS", threads]) | ||
194 | |||
195 | def set_pmake(self, threads): | ||
196 | pmake = "-j %s" % threads | ||
197 | self.server.runCommand(["setVariable", "BB_NUMBER_THREADS", pmake]) | ||
198 | |||
199 | def run_build(self, tgts): | ||
200 | self.building = "image" | ||
201 | targets = [] | ||
202 | targets.append(tgts) | ||
203 | if self.build_toolchain and self.build_toolchain_headers: | ||
204 | targets = ["meta-toolchain-sdk"] + targets | ||
205 | elif self.build_toolchain: | ||
206 | targets = ["meta-toolchain"] + targets | ||
135 | self.server.runCommand(["buildTargets", targets, "build"]) | 207 | self.server.runCommand(["buildTargets", targets, "build"]) |
136 | 208 | ||
137 | def cancel_build(self): | 209 | def build_packages(self, pkgs): |
138 | # Note: this may not be the right way to stop an in-progress build | 210 | self.building = "packages" |
139 | self.server.runCommand(["stateStop"]) | 211 | if 'meta-toolchain' in self.build_queue: |
212 | self.build_queue.remove('meta-toolchain') | ||
213 | pkgs.extend('meta-toolchain') | ||
214 | self.server.runCommand(["buildTargets", pkgs, "build"]) | ||
215 | |||
216 | def build_file(self, image): | ||
217 | self.building = "image" | ||
218 | self.server.runCommand(["buildFile", image, "build"]) | ||
219 | |||
220 | def cancel_build(self, force=False): | ||
221 | if force: | ||
222 | # Force the cooker to stop as quickly as possible | ||
223 | self.server.runCommand(["stateStop"]) | ||
224 | else: | ||
225 | # Wait for tasks to complete before shutting down, this helps | ||
226 | # leave the workdir in a usable state | ||
227 | self.server.runCommand(["stateShutdown"]) | ||
228 | |||
229 | def toggle_gplv3(self, excluded): | ||
230 | if self.gplv3_excluded != excluded: | ||
231 | self.gplv3_excluded = excluded | ||
232 | if excluded: | ||
233 | self.server.runCommand(["setVariable", "INCOMPATIBLE_LICENSE", "GPLv3"]) | ||
234 | else: | ||
235 | self.server.runCommand(["setVariable", "INCOMPATIBLE_LICENSE", ""]) | ||
236 | |||
237 | def toggle_toolchain(self, enabled): | ||
238 | if self.build_toolchain != enabled: | ||
239 | self.build_toolchain = enabled | ||
240 | |||
241 | def toggle_toolchain_headers(self, enabled): | ||
242 | if self.build_toolchain_headers != enabled: | ||
243 | self.build_toolchain_headers = enabled | ||
244 | |||
245 | def queue_image_recipe_path(self, path): | ||
246 | self.build_queue.append(path) | ||
247 | |||
248 | def build_complete_cb(self, running_build): | ||
249 | if len(self.build_queue) > 0: | ||
250 | next = self.build_queue.pop(0) | ||
251 | if next.endswith('.bb'): | ||
252 | self.build_file(next) | ||
253 | self.building = 'image' | ||
254 | self.build_file(next) | ||
255 | else: | ||
256 | self.build_packages(next.split(" ")) | ||
257 | else: | ||
258 | self.building = None | ||
259 | self.emit("build-complete") | ||
260 | |||
261 | def set_image_output_type(self, output_type): | ||
262 | self.server.runCommand(["setVariable", "IMAGE_FSTYPES", output_type]) | ||
263 | |||
264 | def get_image_deploy_dir(self): | ||
265 | return self.server.runCommand(["getVariable", "DEPLOY_DIR_IMAGE"]) | ||
diff --git a/bitbake/lib/bb/ui/crumbs/hobprefs.py b/bitbake/lib/bb/ui/crumbs/hobprefs.py new file mode 100644 index 0000000000..f186410720 --- /dev/null +++ b/bitbake/lib/bb/ui/crumbs/hobprefs.py | |||
@@ -0,0 +1,293 @@ | |||
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 gtk | ||
22 | from bb.ui.crumbs.configurator import Configurator | ||
23 | |||
24 | class HobPrefs(gtk.Dialog): | ||
25 | """ | ||
26 | """ | ||
27 | def empty_combo_text(self, combo_text): | ||
28 | model = combo_text.get_model() | ||
29 | if model: | ||
30 | model.clear() | ||
31 | |||
32 | def output_type_changed_cb(self, combo, handler): | ||
33 | ot = combo.get_active_text() | ||
34 | if ot != self.curr_output_type: | ||
35 | self.curr_output_type = ot | ||
36 | handler.set_image_output_type(ot) | ||
37 | |||
38 | def sdk_machine_combo_changed_cb(self, combo, handler): | ||
39 | sdk_mach = combo.get_active_text() | ||
40 | if sdk_mach != self.curr_sdk_mach: | ||
41 | self.curr_sdk_mach = sdk_mach | ||
42 | self.configurator.setLocalConfVar('SDKMACHINE', sdk_mach) | ||
43 | handler.set_sdk_machine(sdk_mach) | ||
44 | |||
45 | def update_sdk_machines(self, handler, sdk_machines): | ||
46 | active = 0 | ||
47 | # disconnect the signal handler before updating the combo model | ||
48 | if self.sdk_machine_handler_id: | ||
49 | self.sdk_machine_combo.disconnect(self.sdk_machine_handler_id) | ||
50 | self.sdk_machine_handler_id = None | ||
51 | |||
52 | self.empty_combo_text(self.sdk_machine_combo) | ||
53 | for sdk_machine in sdk_machines: | ||
54 | self.sdk_machine_combo.append_text(sdk_machine) | ||
55 | if sdk_machine == self.curr_sdk_mach: | ||
56 | self.sdk_machine_combo.set_active(active) | ||
57 | active = active + 1 | ||
58 | |||
59 | self.sdk_machine_handler_id = self.sdk_machine_combo.connect("changed", self.sdk_machine_combo_changed_cb, handler) | ||
60 | |||
61 | def distro_combo_changed_cb(self, combo, handler): | ||
62 | distro = combo.get_active_text() | ||
63 | if distro != self.curr_distro: | ||
64 | self.curr_distro = distro | ||
65 | self.configurator.setLocalConfVar('DISTRO', distro) | ||
66 | handler.set_distro(distro) | ||
67 | self.reload_required = True | ||
68 | |||
69 | def update_distros(self, handler, distros): | ||
70 | active = 0 | ||
71 | # disconnect the signal handler before updating combo model | ||
72 | if self.distro_handler_id: | ||
73 | self.distro_combo.disconnect(self.distro_handler_id) | ||
74 | self.distro_handler_id = None | ||
75 | |||
76 | self.empty_combo_text(self.distro_combo) | ||
77 | for distro in distros: | ||
78 | self.distro_combo.append_text(distro) | ||
79 | if distro == self.curr_distro: | ||
80 | self.distro_combo.set_active(active) | ||
81 | active = active + 1 | ||
82 | |||
83 | self.distro_handler_id = self.distro_combo.connect("changed", self.distro_combo_changed_cb, handler) | ||
84 | |||
85 | def package_format_combo_changed_cb(self, combo, handler): | ||
86 | package_format = combo.get_active_text() | ||
87 | if package_format != self.curr_package_format: | ||
88 | self.curr_package_format = package_format | ||
89 | self.configurator.setLocalConfVar('PACKAGE_CLASSES', 'package_%s' % package_format) | ||
90 | handler.set_package_format(package_format) | ||
91 | |||
92 | def update_package_formats(self, handler, formats): | ||
93 | active = 0 | ||
94 | # disconnect the signal handler before updating the model | ||
95 | if self.package_handler_id: | ||
96 | self.package_combo.disconnect(self.package_handler_id) | ||
97 | self.package_handler_id = None | ||
98 | |||
99 | self.empty_combo_text(self.package_combo) | ||
100 | for format in formats: | ||
101 | self.package_combo.append_text(format) | ||
102 | if format == self.curr_package_format: | ||
103 | self.package_combo.set_active(active) | ||
104 | active = active + 1 | ||
105 | |||
106 | self.package_handler_id = self.package_combo.connect("changed", self.package_format_combo_changed_cb, handler) | ||
107 | |||
108 | def include_gplv3_cb(self, toggle): | ||
109 | excluded = toggle.get_active() | ||
110 | self.handler.toggle_gplv3(excluded) | ||
111 | if excluded: | ||
112 | self.configurator.setLocalConfVar('INCOMPATIBLE_LICENSE', 'GPLv3') | ||
113 | else: | ||
114 | self.configurator.setLocalConfVar('INCOMPATIBLE_LICENSE', '') | ||
115 | self.reload_required = True | ||
116 | |||
117 | def change_bb_threads_cb(self, spinner): | ||
118 | val = spinner.get_value_as_int() | ||
119 | self.handler.set_bbthreads(val) | ||
120 | self.configurator.setLocalConfVar('BB_NUMBER_THREADS', val) | ||
121 | |||
122 | def change_make_threads_cb(self, spinner): | ||
123 | val = spinner.get_value_as_int() | ||
124 | self.handler.set_pmake(val) | ||
125 | self.configurator.setLocalConfVar('PARALLEL_MAKE', "-j %s" % val) | ||
126 | |||
127 | def toggle_toolchain_cb(self, check): | ||
128 | enabled = check.get_active() | ||
129 | self.handler.toggle_toolchain(enabled) | ||
130 | |||
131 | def toggle_headers_cb(self, check): | ||
132 | enabled = check.get_active() | ||
133 | self.handler.toggle_toolchain_headers(enabled) | ||
134 | |||
135 | def set_parent_window(self, parent): | ||
136 | self.set_transient_for(parent) | ||
137 | |||
138 | def write_changes(self): | ||
139 | self.configurator.writeLocalConf() | ||
140 | |||
141 | def prefs_response_cb(self, dialog, response): | ||
142 | if self.reload_required: | ||
143 | glib.idle_add(self.handler.reload_data) | ||
144 | |||
145 | def __init__(self, configurator, handler, curr_sdk_mach, curr_distro, pclass, | ||
146 | cpu_cnt, pmake, bbthread, image_types): | ||
147 | """ | ||
148 | """ | ||
149 | gtk.Dialog.__init__(self, "Preferences", None, | ||
150 | gtk.DIALOG_DESTROY_WITH_PARENT, | ||
151 | (gtk.STOCK_CLOSE, gtk.RESPONSE_OK)) | ||
152 | |||
153 | self.set_border_width(6) | ||
154 | self.vbox.set_property("spacing", 12) | ||
155 | self.action_area.set_property("spacing", 12) | ||
156 | self.action_area.set_property("border-width", 6) | ||
157 | |||
158 | self.handler = handler | ||
159 | self.configurator = configurator | ||
160 | |||
161 | self.curr_sdk_mach = curr_sdk_mach | ||
162 | self.curr_distro = curr_distro | ||
163 | self.curr_package_format = pclass | ||
164 | self.curr_output_type = None | ||
165 | self.cpu_cnt = cpu_cnt | ||
166 | self.pmake = pmake | ||
167 | self.bbthread = bbthread | ||
168 | self.reload_required = False | ||
169 | self.distro_handler_id = None | ||
170 | self.sdk_machine_handler_id = None | ||
171 | self.package_handler_id = None | ||
172 | |||
173 | left = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL) | ||
174 | right = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL) | ||
175 | |||
176 | label = gtk.Label() | ||
177 | label.set_markup("<b>Policy</b>") | ||
178 | label.show() | ||
179 | frame = gtk.Frame() | ||
180 | frame.set_label_widget(label) | ||
181 | frame.set_shadow_type(gtk.SHADOW_NONE) | ||
182 | frame.show() | ||
183 | self.vbox.pack_start(frame) | ||
184 | pbox = gtk.VBox(False, 12) | ||
185 | pbox.show() | ||
186 | frame.add(pbox) | ||
187 | hbox = gtk.HBox(False, 12) | ||
188 | hbox.show() | ||
189 | pbox.pack_start(hbox, expand=False, fill=False, padding=6) | ||
190 | # Distro selector | ||
191 | label = gtk.Label("Distribution:") | ||
192 | label.show() | ||
193 | hbox.pack_start(label, expand=False, fill=False, padding=6) | ||
194 | self.distro_combo = gtk.combo_box_new_text() | ||
195 | self.distro_combo.set_tooltip_text("Select the Yocto distribution you would like to use") | ||
196 | self.distro_combo.show() | ||
197 | hbox.pack_start(self.distro_combo, expand=False, fill=False, padding=6) | ||
198 | # Exclude GPLv3 | ||
199 | check = gtk.CheckButton("Exclude GPLv3 packages") | ||
200 | check.set_tooltip_text("Check this box to prevent GPLv3 packages from being included in your image") | ||
201 | check.show() | ||
202 | check.connect("toggled", self.include_gplv3_cb) | ||
203 | hbox.pack_start(check, expand=False, fill=False, padding=6) | ||
204 | hbox = gtk.HBox(False, 12) | ||
205 | hbox.show() | ||
206 | pbox.pack_start(hbox, expand=False, fill=False, padding=6) | ||
207 | # Package format selector | ||
208 | label = gtk.Label("Package format:") | ||
209 | label.show() | ||
210 | hbox.pack_start(label, expand=False, fill=False, padding=6) | ||
211 | self.package_combo = gtk.combo_box_new_text() | ||
212 | self.package_combo.set_tooltip_text("Select the package format you would like to use in your image") | ||
213 | self.package_combo.show() | ||
214 | hbox.pack_start(self.package_combo, expand=False, fill=False, padding=6) | ||
215 | # Image output type selector | ||
216 | label = gtk.Label("Image output type:") | ||
217 | label.show() | ||
218 | hbox.pack_start(label, expand=False, fill=False, padding=6) | ||
219 | output_combo = gtk.combo_box_new_text() | ||
220 | if image_types: | ||
221 | for it in image_types.split(" "): | ||
222 | output_combo.append_text(it) | ||
223 | output_combo.connect("changed", self.output_type_changed_cb, handler) | ||
224 | else: | ||
225 | output_combo.set_sensitive(False) | ||
226 | output_combo.show() | ||
227 | hbox.pack_start(output_combo) | ||
228 | # BitBake | ||
229 | label = gtk.Label() | ||
230 | label.set_markup("<b>BitBake</b>") | ||
231 | label.show() | ||
232 | frame = gtk.Frame() | ||
233 | frame.set_label_widget(label) | ||
234 | frame.set_shadow_type(gtk.SHADOW_NONE) | ||
235 | frame.show() | ||
236 | self.vbox.pack_start(frame) | ||
237 | pbox = gtk.VBox(False, 12) | ||
238 | pbox.show() | ||
239 | frame.add(pbox) | ||
240 | hbox = gtk.HBox(False, 12) | ||
241 | hbox.show() | ||
242 | pbox.pack_start(hbox, expand=False, fill=False, padding=6) | ||
243 | label = gtk.Label("BitBake threads:") | ||
244 | label.show() | ||
245 | spin_max = 9 #self.cpu_cnt * 3 | ||
246 | hbox.pack_start(label, expand=False, fill=False, padding=6) | ||
247 | bbadj = gtk.Adjustment(value=self.bbthread, lower=1, upper=spin_max, step_incr=1) | ||
248 | bbspinner = gtk.SpinButton(adjustment=bbadj, climb_rate=1, digits=0) | ||
249 | bbspinner.show() | ||
250 | bbspinner.connect("value-changed", self.change_bb_threads_cb) | ||
251 | hbox.pack_start(bbspinner, expand=False, fill=False, padding=6) | ||
252 | label = gtk.Label("Make threads:") | ||
253 | label.show() | ||
254 | hbox.pack_start(label, expand=False, fill=False, padding=6) | ||
255 | madj = gtk.Adjustment(value=self.pmake, lower=1, upper=spin_max, step_incr=1) | ||
256 | makespinner = gtk.SpinButton(adjustment=madj, climb_rate=1, digits=0) | ||
257 | makespinner.connect("value-changed", self.change_make_threads_cb) | ||
258 | makespinner.show() | ||
259 | hbox.pack_start(makespinner, expand=False, fill=False, padding=6) | ||
260 | # Toolchain | ||
261 | label = gtk.Label() | ||
262 | label.set_markup("<b>External Toolchain</b>") | ||
263 | label.show() | ||
264 | frame = gtk.Frame() | ||
265 | frame.set_label_widget(label) | ||
266 | frame.set_shadow_type(gtk.SHADOW_NONE) | ||
267 | frame.show() | ||
268 | self.vbox.pack_start(frame) | ||
269 | pbox = gtk.VBox(False, 12) | ||
270 | pbox.show() | ||
271 | frame.add(pbox) | ||
272 | hbox = gtk.HBox(False, 12) | ||
273 | hbox.show() | ||
274 | pbox.pack_start(hbox, expand=False, fill=False, padding=6) | ||
275 | toolcheck = gtk.CheckButton("Build external development toolchain with image") | ||
276 | toolcheck.show() | ||
277 | toolcheck.connect("toggled", self.toggle_toolchain_cb) | ||
278 | hbox.pack_start(toolcheck, expand=False, fill=False, padding=6) | ||
279 | hbox = gtk.HBox(False, 12) | ||
280 | hbox.show() | ||
281 | pbox.pack_start(hbox, expand=False, fill=False, padding=6) | ||
282 | label = gtk.Label("Toolchain host:") | ||
283 | label.show() | ||
284 | hbox.pack_start(label, expand=False, fill=False, padding=6) | ||
285 | self.sdk_machine_combo = gtk.combo_box_new_text() | ||
286 | self.sdk_machine_combo.set_tooltip_text("Select the host architecture of the external machine") | ||
287 | self.sdk_machine_combo.show() | ||
288 | hbox.pack_start(self.sdk_machine_combo, expand=False, fill=False, padding=6) | ||
289 | headerscheck = gtk.CheckButton("Include development headers with toolchain") | ||
290 | headerscheck.show() | ||
291 | headerscheck.connect("toggled", self.toggle_headers_cb) | ||
292 | hbox.pack_start(headerscheck, expand=False, fill=False, padding=6) | ||
293 | self.connect("response", self.prefs_response_cb) | ||
diff --git a/bitbake/lib/bb/ui/crumbs/layereditor.py b/bitbake/lib/bb/ui/crumbs/layereditor.py new file mode 100644 index 0000000000..76a2eb536f --- /dev/null +++ b/bitbake/lib/bb/ui/crumbs/layereditor.py | |||
@@ -0,0 +1,136 @@ | |||
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.configurator import Configurator | ||
24 | |||
25 | class LayerEditor(gtk.Dialog): | ||
26 | """ | ||
27 | Gtk+ Widget for enabling and disabling layers. | ||
28 | Layers are added through using an open dialog to find the layer.conf | ||
29 | Disabled layers are deleted from conf/bblayers.conf | ||
30 | """ | ||
31 | def __init__(self, configurator, parent=None): | ||
32 | gtk.Dialog.__init__(self, "Layers", None, | ||
33 | gtk.DIALOG_DESTROY_WITH_PARENT, | ||
34 | (gtk.STOCK_CLOSE, gtk.RESPONSE_OK)) | ||
35 | |||
36 | # We want to show a little more of the treeview in the default, | ||
37 | # emptier, case | ||
38 | self.set_size_request(-1, 300) | ||
39 | self.set_border_width(6) | ||
40 | self.vbox.set_property("spacing", 0) | ||
41 | self.action_area.set_property("border-width", 6) | ||
42 | |||
43 | self.configurator = configurator | ||
44 | self.newly_added = {} | ||
45 | |||
46 | # Label to inform users that meta is enabled but that you can't | ||
47 | # disable it as it'd be a *bad* idea | ||
48 | msg = "As the core of the build system the <i>meta</i> layer must always be included and therefore can't be viewed or edited here." | ||
49 | lbl = gtk.Label() | ||
50 | lbl.show() | ||
51 | lbl.set_use_markup(True) | ||
52 | lbl.set_markup(msg) | ||
53 | lbl.set_line_wrap(True) | ||
54 | lbl.set_justify(gtk.JUSTIFY_FILL) | ||
55 | self.vbox.pack_start(lbl, expand=False, fill=False, padding=6) | ||
56 | |||
57 | # Create a treeview in which to list layers | ||
58 | # ListStore of Name, Path, Enabled | ||
59 | self.layer_store = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_BOOLEAN) | ||
60 | self.tv = gtk.TreeView(self.layer_store) | ||
61 | self.tv.set_headers_visible(True) | ||
62 | |||
63 | col0 = gtk.TreeViewColumn('Name') | ||
64 | self.tv.append_column(col0) | ||
65 | col1 = gtk.TreeViewColumn('Path') | ||
66 | self.tv.append_column(col1) | ||
67 | col2 = gtk.TreeViewColumn('Enabled') | ||
68 | self.tv.append_column(col2) | ||
69 | |||
70 | cell0 = gtk.CellRendererText() | ||
71 | col0.pack_start(cell0, True) | ||
72 | col0.set_attributes(cell0, text=0) | ||
73 | cell1 = gtk.CellRendererText() | ||
74 | col1.pack_start(cell1, True) | ||
75 | col1.set_attributes(cell1, text=1) | ||
76 | cell2 = gtk.CellRendererToggle() | ||
77 | cell2.connect("toggled", self._toggle_layer_cb) | ||
78 | col2.pack_start(cell2, True) | ||
79 | col2.set_attributes(cell2, active=2) | ||
80 | |||
81 | self.tv.show() | ||
82 | self.vbox.pack_start(self.tv, expand=True, fill=True, padding=0) | ||
83 | |||
84 | tb = gtk.Toolbar() | ||
85 | tb.set_icon_size(gtk.ICON_SIZE_SMALL_TOOLBAR) | ||
86 | tb.set_style(gtk.TOOLBAR_BOTH) | ||
87 | tb.set_tooltips(True) | ||
88 | tb.show() | ||
89 | icon = gtk.Image() | ||
90 | icon.set_from_stock(gtk.STOCK_ADD, gtk.ICON_SIZE_SMALL_TOOLBAR) | ||
91 | icon.show() | ||
92 | tb.insert_item("Add Layer", "Add new layer", None, icon, | ||
93 | self._find_layer_cb, None, -1) | ||
94 | self.vbox.pack_start(tb, expand=False, fill=False, padding=0) | ||
95 | |||
96 | def set_parent_window(self, parent): | ||
97 | self.set_transient_for(parent) | ||
98 | |||
99 | def load_current_layers(self, data): | ||
100 | for layer, path in self.configurator.enabled_layers.items(): | ||
101 | if layer != 'meta': | ||
102 | self.layer_store.append([layer, path, True]) | ||
103 | |||
104 | def save_current_layers(self): | ||
105 | self.configurator.writeLayerConf() | ||
106 | |||
107 | def _toggle_layer_cb(self, cell, path): | ||
108 | name = self.layer_store[path][0] | ||
109 | toggle = not self.layer_store[path][2] | ||
110 | if toggle: | ||
111 | self.configurator.addLayer(name, path) | ||
112 | else: | ||
113 | self.configurator.disableLayer(name) | ||
114 | self.layer_store[path][2] = toggle | ||
115 | |||
116 | def _find_layer_cb(self, button): | ||
117 | self.find_layer(self) | ||
118 | |||
119 | def find_layer(self, parent): | ||
120 | dialog = gtk.FileChooserDialog("Add new layer", parent, | ||
121 | gtk.FILE_CHOOSER_ACTION_OPEN, | ||
122 | (gtk.STOCK_CANCEL, gtk.RESPONSE_NO, | ||
123 | gtk.STOCK_OPEN, gtk.RESPONSE_YES)) | ||
124 | label = gtk.Label("Select the layer.conf of the layer you wish to add") | ||
125 | label.show() | ||
126 | dialog.set_extra_widget(label) | ||
127 | response = dialog.run() | ||
128 | path = dialog.get_filename() | ||
129 | dialog.destroy() | ||
130 | |||
131 | if response == gtk.RESPONSE_YES: | ||
132 | # FIXME: verify we've actually got a layer conf? | ||
133 | if path.endswith(".conf"): | ||
134 | name, layerpath = self.configurator.addLayerConf(path) | ||
135 | self.newly_added[name] = layerpath | ||
136 | self.layer_store.append([name, layerpath, True]) | ||
diff --git a/bitbake/lib/bb/ui/crumbs/runningbuild.py b/bitbake/lib/bb/ui/crumbs/runningbuild.py index 70fd57effc..bdab34040c 100644 --- a/bitbake/lib/bb/ui/crumbs/runningbuild.py +++ b/bitbake/lib/bb/ui/crumbs/runningbuild.py | |||
@@ -47,12 +47,18 @@ class RunningBuildModel (gtk.TreeStore): | |||
47 | 47 | ||
48 | class RunningBuild (gobject.GObject): | 48 | class RunningBuild (gobject.GObject): |
49 | __gsignals__ = { | 49 | __gsignals__ = { |
50 | 'build-started' : (gobject.SIGNAL_RUN_LAST, | ||
51 | gobject.TYPE_NONE, | ||
52 | ()), | ||
50 | 'build-succeeded' : (gobject.SIGNAL_RUN_LAST, | 53 | 'build-succeeded' : (gobject.SIGNAL_RUN_LAST, |
51 | gobject.TYPE_NONE, | 54 | gobject.TYPE_NONE, |
52 | ()), | 55 | ()), |
53 | 'build-failed' : (gobject.SIGNAL_RUN_LAST, | 56 | 'build-failed' : (gobject.SIGNAL_RUN_LAST, |
54 | gobject.TYPE_NONE, | 57 | gobject.TYPE_NONE, |
55 | ()) | 58 | ()), |
59 | 'build-complete' : (gobject.SIGNAL_RUN_LAST, | ||
60 | gobject.TYPE_NONE, | ||
61 | ()) | ||
56 | } | 62 | } |
57 | pids_to_task = {} | 63 | pids_to_task = {} |
58 | tasks_to_iter = {} | 64 | tasks_to_iter = {} |
@@ -201,6 +207,7 @@ class RunningBuild (gobject.GObject): | |||
201 | 207 | ||
202 | elif isinstance(event, bb.event.BuildStarted): | 208 | elif isinstance(event, bb.event.BuildStarted): |
203 | 209 | ||
210 | self.emit("build-started") | ||
204 | self.model.prepend(None, (None, | 211 | self.model.prepend(None, (None, |
205 | None, | 212 | None, |
206 | None, | 213 | None, |
@@ -218,6 +225,9 @@ class RunningBuild (gobject.GObject): | |||
218 | Colors.OK, | 225 | Colors.OK, |
219 | 0)) | 226 | 0)) |
220 | 227 | ||
228 | # Emit a generic "build-complete" signal for things wishing to | ||
229 | # handle when the build is finished | ||
230 | self.emit("build-complete") | ||
221 | # Emit the appropriate signal depending on the number of failures | 231 | # Emit the appropriate signal depending on the number of failures |
222 | if (failures >= 1): | 232 | if (failures >= 1): |
223 | self.emit ("build-failed") | 233 | self.emit ("build-failed") |
diff --git a/bitbake/lib/bb/ui/crumbs/tasklistmodel.py b/bitbake/lib/bb/ui/crumbs/tasklistmodel.py index a83a176ddc..d9829861bb 100644 --- a/bitbake/lib/bb/ui/crumbs/tasklistmodel.py +++ b/bitbake/lib/bb/ui/crumbs/tasklistmodel.py | |||
@@ -20,6 +20,58 @@ | |||
20 | 20 | ||
21 | import gtk | 21 | import gtk |
22 | import gobject | 22 | import gobject |
23 | import re | ||
24 | |||
25 | class BuildRep(gobject.GObject): | ||
26 | |||
27 | def __init__(self, userpkgs, allpkgs, base_image=None): | ||
28 | gobject.GObject.__init__(self) | ||
29 | self.base_image = base_image | ||
30 | self.allpkgs = allpkgs | ||
31 | self.userpkgs = userpkgs | ||
32 | |||
33 | def loadRecipe(self, pathname): | ||
34 | contents = [] | ||
35 | packages = "" | ||
36 | base_image = "" | ||
37 | |||
38 | with open(pathname, 'r') as f: | ||
39 | contents = f.readlines() | ||
40 | |||
41 | pkg_pattern = "^\s*(IMAGE_INSTALL)\s*([+=.?]+)\s*(\"\S*\")" | ||
42 | img_pattern = "^\s*(require)\s+(\S+.bb)" | ||
43 | |||
44 | for line in contents: | ||
45 | matchpkg = re.search(pkg_pattern, line) | ||
46 | matchimg = re.search(img_pattern, line) | ||
47 | if matchpkg: | ||
48 | packages = packages + matchpkg.group(3).strip('"') | ||
49 | if matchimg: | ||
50 | base_image = os.path.basename(matchimg.group(2)).split(".")[0] | ||
51 | |||
52 | self.base_image = base_image | ||
53 | self.userpkgs = packages | ||
54 | |||
55 | def writeRecipe(self, writepath, model): | ||
56 | # FIXME: Need a better way to determine meta_path... | ||
57 | template = """ | ||
58 | # Recipe generated by the HOB | ||
59 | |||
60 | require %s.bb | ||
61 | |||
62 | IMAGE_INSTALL += "%s" | ||
63 | """ | ||
64 | meta_path = model.find_image_path(self.base_image) | ||
65 | |||
66 | recipe = template % (meta_path, self.userpkgs) | ||
67 | |||
68 | if os.path.exists(writepath): | ||
69 | os.rename(writepath, "%s~" % writepath) | ||
70 | |||
71 | with open(writepath, 'w') as r: | ||
72 | r.write(recipe) | ||
73 | |||
74 | return writepath | ||
23 | 75 | ||
24 | class TaskListModel(gtk.ListStore): | 76 | class TaskListModel(gtk.ListStore): |
25 | """ | 77 | """ |
@@ -28,12 +80,18 @@ class TaskListModel(gtk.ListStore): | |||
28 | providing convenience functions to access gtk.TreeModel subclasses which | 80 | providing convenience functions to access gtk.TreeModel subclasses which |
29 | provide filtered views of the data. | 81 | provide filtered views of the data. |
30 | """ | 82 | """ |
31 | (COL_NAME, COL_DESC, COL_LIC, COL_GROUP, COL_DEPS, COL_BINB, COL_TYPE, COL_INC) = range(8) | 83 | (COL_NAME, COL_DESC, COL_LIC, COL_GROUP, COL_DEPS, COL_BINB, COL_TYPE, COL_INC, COL_IMG, COL_PATH) = range(10) |
32 | 84 | ||
33 | __gsignals__ = { | 85 | __gsignals__ = { |
34 | "tasklist-populated" : (gobject.SIGNAL_RUN_LAST, | 86 | "tasklist-populated" : (gobject.SIGNAL_RUN_LAST, |
35 | gobject.TYPE_NONE, | 87 | gobject.TYPE_NONE, |
36 | ()) | 88 | ()), |
89 | "contents-changed" : (gobject.SIGNAL_RUN_LAST, | ||
90 | gobject.TYPE_NONE, | ||
91 | (gobject.TYPE_INT,)), | ||
92 | "image-changed" : (gobject.SIGNAL_RUN_LAST, | ||
93 | gobject.TYPE_NONE, | ||
94 | (gobject.TYPE_STRING,)), | ||
37 | } | 95 | } |
38 | 96 | ||
39 | """ | 97 | """ |
@@ -43,6 +101,7 @@ class TaskListModel(gtk.ListStore): | |||
43 | self.tasks = None | 101 | self.tasks = None |
44 | self.packages = None | 102 | self.packages = None |
45 | self.images = None | 103 | self.images = None |
104 | self.selected_image = None | ||
46 | 105 | ||
47 | gtk.ListStore.__init__ (self, | 106 | gtk.ListStore.__init__ (self, |
48 | gobject.TYPE_STRING, | 107 | gobject.TYPE_STRING, |
@@ -52,7 +111,22 @@ class TaskListModel(gtk.ListStore): | |||
52 | gobject.TYPE_STRING, | 111 | gobject.TYPE_STRING, |
53 | gobject.TYPE_STRING, | 112 | gobject.TYPE_STRING, |
54 | gobject.TYPE_STRING, | 113 | gobject.TYPE_STRING, |
55 | gobject.TYPE_BOOLEAN) | 114 | gobject.TYPE_BOOLEAN, |
115 | gobject.TYPE_BOOLEAN, | ||
116 | gobject.TYPE_STRING) | ||
117 | |||
118 | def contents_changed_cb(self, tree_model, path, it=None): | ||
119 | pkg_cnt = self.contents.iter_n_children(None) | ||
120 | self.emit("contents-changed", pkg_cnt) | ||
121 | |||
122 | def contents_model_filter(self, model, it): | ||
123 | if not model.get_value(it, self.COL_INC) or model.get_value(it, self.COL_TYPE) == 'image': | ||
124 | return False | ||
125 | name = model.get_value(it, self.COL_NAME) | ||
126 | if name.endswith('-native') or name.endswith('-cross'): | ||
127 | return False | ||
128 | else: | ||
129 | return True | ||
56 | 130 | ||
57 | """ | 131 | """ |
58 | Create, if required, and return a filtered gtk.TreeModel | 132 | Create, if required, and return a filtered gtk.TreeModel |
@@ -62,7 +136,9 @@ class TaskListModel(gtk.ListStore): | |||
62 | def contents_model(self): | 136 | def contents_model(self): |
63 | if not self.contents: | 137 | if not self.contents: |
64 | self.contents = self.filter_new() | 138 | self.contents = self.filter_new() |
65 | self.contents.set_visible_column(self.COL_INC) | 139 | self.contents.set_visible_func(self.contents_model_filter) |
140 | self.contents.connect("row-inserted", self.contents_changed_cb) | ||
141 | self.contents.connect("row-deleted", self.contents_changed_cb) | ||
66 | return self.contents | 142 | return self.contents |
67 | 143 | ||
68 | """ | 144 | """ |
@@ -107,10 +183,10 @@ class TaskListModel(gtk.ListStore): | |||
107 | Helper function to determine whether an item is a package | 183 | Helper function to determine whether an item is a package |
108 | """ | 184 | """ |
109 | def package_model_filter(self, model, it): | 185 | def package_model_filter(self, model, it): |
110 | if model.get_value(it, self.COL_TYPE) == 'package': | 186 | if model.get_value(it, self.COL_TYPE) != 'package': |
111 | return True | ||
112 | else: | ||
113 | return False | 187 | return False |
188 | else: | ||
189 | return True | ||
114 | 190 | ||
115 | """ | 191 | """ |
116 | Create, if required, and return a filtered gtk.TreeModel | 192 | Create, if required, and return a filtered gtk.TreeModel |
@@ -129,33 +205,78 @@ class TaskListModel(gtk.ListStore): | |||
129 | to notify any listeners that the model is ready | 205 | to notify any listeners that the model is ready |
130 | """ | 206 | """ |
131 | def populate(self, event_model): | 207 | def populate(self, event_model): |
208 | # First clear the model, in case repopulating | ||
209 | self.clear() | ||
132 | for item in event_model["pn"]: | 210 | for item in event_model["pn"]: |
133 | atype = 'package' | 211 | atype = 'package' |
134 | name = item | 212 | name = item |
135 | summary = event_model["pn"][item]["summary"] | 213 | summary = event_model["pn"][item]["summary"] |
136 | license = event_model["pn"][item]["license"] | 214 | lic = event_model["pn"][item]["license"] |
137 | group = event_model["pn"][item]["section"] | 215 | group = event_model["pn"][item]["section"] |
138 | 216 | filename = event_model["pn"][item]["filename"] | |
139 | depends = event_model["depends"].get(item, "") | 217 | depends = event_model["depends"].get(item, "") |
140 | rdepends = event_model["rdepends-pn"].get(item, "") | 218 | rdepends = event_model["rdepends-pn"].get(item, "") |
141 | depends = depends + rdepends | 219 | if rdepends: |
220 | for rdep in rdepends: | ||
221 | if event_model["packages"].get(rdep, ""): | ||
222 | pn = event_model["packages"][rdep].get("pn", "") | ||
223 | if pn: | ||
224 | depends.append(pn) | ||
225 | |||
142 | self.squish(depends) | 226 | self.squish(depends) |
143 | deps = " ".join(depends) | 227 | deps = " ".join(depends) |
144 | 228 | ||
145 | if name.count('task-') > 0: | 229 | if name.count('task-') > 0: |
146 | atype = 'task' | 230 | atype = 'task' |
147 | elif name.count('-image-') > 0: | 231 | elif name.count('-image-') > 0: |
148 | atype = 'image' | 232 | atype = 'image' |
149 | 233 | ||
150 | self.set(self.append(), self.COL_NAME, name, self.COL_DESC, summary, | 234 | self.set(self.append(), self.COL_NAME, name, self.COL_DESC, summary, |
151 | self.COL_LIC, license, self.COL_GROUP, group, | 235 | self.COL_LIC, lic, self.COL_GROUP, group, |
152 | self.COL_DEPS, deps, self.COL_BINB, "", | 236 | self.COL_DEPS, deps, self.COL_BINB, "", |
153 | self.COL_TYPE, atype, self.COL_INC, False) | 237 | self.COL_TYPE, atype, self.COL_INC, False, |
154 | 238 | self.COL_IMG, False, self.COL_PATH, filename) | |
239 | |||
155 | self.emit("tasklist-populated") | 240 | self.emit("tasklist-populated") |
156 | 241 | ||
157 | """ | 242 | """ |
158 | squish lst so that it doesn't contain any duplicates | 243 | Load a BuildRep into the model |
244 | """ | ||
245 | def load_image_rep(self, rep): | ||
246 | # Unset everything | ||
247 | it = self.get_iter_first() | ||
248 | while it: | ||
249 | path = self.get_path(it) | ||
250 | self[path][self.COL_INC] = False | ||
251 | self[path][self.COL_IMG] = False | ||
252 | it = self.iter_next(it) | ||
253 | |||
254 | # Iterate the images and disable them all | ||
255 | it = self.images.get_iter_first() | ||
256 | while it: | ||
257 | path = self.images.convert_path_to_child_path(self.images.get_path(it)) | ||
258 | name = self[path][self.COL_NAME] | ||
259 | if name == rep.base_image: | ||
260 | self.include_item(path, image_contents=True) | ||
261 | else: | ||
262 | self[path][self.COL_INC] = False | ||
263 | it = self.images.iter_next(it) | ||
264 | |||
265 | # Mark all of the additional packages for inclusion | ||
266 | packages = rep.packages.split(" ") | ||
267 | it = self.get_iter_first() | ||
268 | while it: | ||
269 | path = self.get_path(it) | ||
270 | name = self[path][self.COL_NAME] | ||
271 | if name in packages: | ||
272 | self.include_item(path) | ||
273 | packages.remove(name) | ||
274 | it = self.iter_next(it) | ||
275 | |||
276 | self.emit("image-changed", rep.base_image) | ||
277 | |||
278 | """ | ||
279 | squish lst so that it doesn't contain any duplicate entries | ||
159 | """ | 280 | """ |
160 | def squish(self, lst): | 281 | def squish(self, lst): |
161 | seen = {} | 282 | seen = {} |
@@ -173,56 +294,59 @@ class TaskListModel(gtk.ListStore): | |||
173 | self[path][self.COL_INC] = False | 294 | self[path][self.COL_INC] = False |
174 | 295 | ||
175 | """ | 296 | """ |
297 | recursively called to mark the item at opath and any package which | ||
298 | depends on it for removal | ||
176 | """ | 299 | """ |
177 | def mark(self, path): | 300 | def mark(self, opath): |
178 | name = self[path][self.COL_NAME] | ||
179 | it = self.get_iter_first() | ||
180 | removals = [] | 301 | removals = [] |
181 | #print("Removing %s" % name) | 302 | it = self.get_iter_first() |
303 | name = self[opath][self.COL_NAME] | ||
182 | 304 | ||
183 | self.remove_item_path(path) | 305 | self.remove_item_path(opath) |
184 | 306 | ||
185 | # Remove all dependent packages, update binb | 307 | # Remove all dependent packages, update binb |
186 | while it: | 308 | while it: |
187 | path = self.get_path(it) | 309 | path = self.get_path(it) |
188 | # FIXME: need to ensure partial name matching doesn't happen, regexp? | 310 | inc = self[path][self.COL_INC] |
189 | if self[path][self.COL_INC] and self[path][self.COL_DEPS].count(name): | 311 | deps = self[path][self.COL_DEPS] |
190 | #print("%s depended on %s, marking for removal" % (self[path][self.COL_NAME], name)) | 312 | binb = self[path][self.COL_BINB] |
313 | |||
314 | # FIXME: need to ensure partial name matching doesn't happen | ||
315 | if inc and deps.count(name): | ||
191 | # found a dependency, remove it | 316 | # found a dependency, remove it |
192 | self.mark(path) | 317 | self.mark(path) |
193 | if self[path][self.COL_INC] and self[path][self.COL_BINB].count(name): | 318 | if inc and binb.count(name): |
194 | binb = self.find_alt_dependency(self[path][self.COL_NAME]) | 319 | bib = self.find_alt_dependency(name) |
195 | #print("%s was brought in by %s, binb set to %s" % (self[path][self.COL_NAME], name, binb)) | 320 | self[path][self.COL_BINB] = bib |
196 | self[path][self.COL_BINB] = binb | 321 | |
197 | it = self.iter_next(it) | 322 | it = self.iter_next(it) |
198 | 323 | ||
199 | """ | 324 | """ |
325 | Remove items from contents if the have an empty COL_BINB (brought in by) | ||
326 | caused by all packages they are a dependency of being removed. | ||
327 | If the item isn't a package we leave it included. | ||
200 | """ | 328 | """ |
201 | def sweep_up(self): | 329 | def sweep_up(self): |
330 | model = self.contents | ||
202 | removals = [] | 331 | removals = [] |
203 | it = self.get_iter_first() | 332 | it = self.contents.get_iter_first() |
204 | 333 | ||
205 | while it: | 334 | while it: |
206 | path = self.get_path(it) | 335 | binb = model.get_value(it, self.COL_BINB) |
207 | binb = self[path][self.COL_BINB] | 336 | itype = model.get_value(it, self.COL_TYPE) |
208 | if binb == "" or binb is None: | 337 | |
209 | #print("Sweeping up %s" % self[path][self.COL_NAME]) | 338 | if itype == 'package' and not binb: |
210 | if not path in removals: | 339 | opath = model.convert_path_to_child_path(model.get_path(it)) |
211 | removals.extend(path) | 340 | if not opath in removals: |
212 | it = self.iter_next(it) | 341 | removals.extend(opath) |
342 | |||
343 | it = model.iter_next(it) | ||
213 | 344 | ||
214 | while removals: | 345 | while removals: |
215 | path = removals.pop() | 346 | path = removals.pop() |
216 | self.mark(path) | 347 | self.mark(path) |
217 | 348 | ||
218 | """ | 349 | """ |
219 | Remove an item from the contents | ||
220 | """ | ||
221 | def remove_item(self, path): | ||
222 | self.mark(path) | ||
223 | self.sweep_up() | ||
224 | |||
225 | """ | ||
226 | Find the name of an item in the image contents which depends on the item | 350 | Find the name of an item in the image contents which depends on the item |
227 | at contents_path returns either an item name (str) or None | 351 | at contents_path returns either an item name (str) or None |
228 | NOTE: | 352 | NOTE: |
@@ -238,18 +362,11 @@ class TaskListModel(gtk.ListStore): | |||
238 | inc = self[path][self.COL_INC] | 362 | inc = self[path][self.COL_INC] |
239 | if itname != name and inc and deps.count(name) > 0: | 363 | if itname != name and inc and deps.count(name) > 0: |
240 | # if this item depends on the item, return this items name | 364 | # if this item depends on the item, return this items name |
241 | #print("%s depends on %s" % (itname, name)) | ||
242 | return itname | 365 | return itname |
243 | it = self.iter_next(it) | 366 | it = self.iter_next(it) |
244 | return "" | 367 | return "" |
245 | 368 | ||
246 | """ | 369 | """ |
247 | Convert a path in self to a path in the filtered contents model | ||
248 | """ | ||
249 | def contents_path_for_path(self, path): | ||
250 | return self.contents.convert_child_path_to_path(path) | ||
251 | |||
252 | """ | ||
253 | Check the self.contents gtk.TreeModel for an item | 370 | Check the self.contents gtk.TreeModel for an item |
254 | where COL_NAME matches item_name | 371 | where COL_NAME matches item_name |
255 | Returns True if a match is found, False otherwise | 372 | Returns True if a match is found, False otherwise |
@@ -266,25 +383,30 @@ class TaskListModel(gtk.ListStore): | |||
266 | """ | 383 | """ |
267 | Add this item, and any of its dependencies, to the image contents | 384 | Add this item, and any of its dependencies, to the image contents |
268 | """ | 385 | """ |
269 | def include_item(self, item_path, binb=""): | 386 | def include_item(self, item_path, binb="", image_contents=False): |
270 | name = self[item_path][self.COL_NAME] | 387 | name = self[item_path][self.COL_NAME] |
271 | deps = self[item_path][self.COL_DEPS] | 388 | deps = self[item_path][self.COL_DEPS] |
272 | cur_inc = self[item_path][self.COL_INC] | 389 | cur_inc = self[item_path][self.COL_INC] |
273 | #print("Adding %s for %s dependency" % (name, binb)) | ||
274 | if not cur_inc: | 390 | if not cur_inc: |
275 | self[item_path][self.COL_INC] = True | 391 | self[item_path][self.COL_INC] = True |
276 | self[item_path][self.COL_BINB] = binb | 392 | self[item_path][self.COL_BINB] = binb |
393 | # We want to do some magic with things which are brought in by the base | ||
394 | # image so tag them as so | ||
395 | if image_contents: | ||
396 | self[item_path][self.COL_IMG] = True | ||
397 | if self[item_path][self.COL_TYPE] == 'image': | ||
398 | self.selected_image = name | ||
399 | |||
277 | if deps: | 400 | if deps: |
278 | #print("Dependencies of %s are %s" % (name, deps)) | ||
279 | # add all of the deps and set their binb to this item | 401 | # add all of the deps and set their binb to this item |
280 | for dep in deps.split(" "): | 402 | for dep in deps.split(" "): |
281 | # FIXME: this skipping virtuals can't be right? Unless we choose only to show target | ||
282 | # packages? In which case we should handle this server side... | ||
283 | # If the contents model doesn't already contain dep, add it | 403 | # If the contents model doesn't already contain dep, add it |
284 | if not dep.startswith("virtual") and not self.contents_includes_name(dep): | 404 | # We only care to show things which will end up in the |
405 | # resultant image, so filter cross and native recipes | ||
406 | if not self.contents_includes_name(dep) and not dep.endswith("-native") and not dep.endswith("-cross"): | ||
285 | path = self.find_path_for_item(dep) | 407 | path = self.find_path_for_item(dep) |
286 | if path: | 408 | if path: |
287 | self.include_item(path, name) | 409 | self.include_item(path, name, image_contents) |
288 | else: | 410 | else: |
289 | pass | 411 | pass |
290 | 412 | ||
@@ -317,30 +439,78 @@ class TaskListModel(gtk.ListStore): | |||
317 | it = self.contents.get_iter_first() | 439 | it = self.contents.get_iter_first() |
318 | 440 | ||
319 | """ | 441 | """ |
320 | Returns True if one of the selected tasks is an image, False otherwise | 442 | Returns two lists. One of user selected packages and the other containing |
443 | all selected packages | ||
321 | """ | 444 | """ |
322 | def targets_contains_image(self): | 445 | def get_selected_packages(self): |
323 | it = self.images.get_iter_first() | 446 | allpkgs = [] |
447 | userpkgs = [] | ||
448 | |||
449 | it = self.contents.get_iter_first() | ||
324 | while it: | 450 | while it: |
325 | path = self.images.get_path(it) | 451 | sel = self.contents.get_value(it, self.COL_BINB) == "User Selected" |
326 | inc = self.images[path][self.COL_INC] | 452 | name = self.contents.get_value(it, self.COL_NAME) |
327 | if inc: | 453 | allpkgs.append(name) |
328 | return True | 454 | if sel: |
329 | it = self.images.iter_next(it) | 455 | userpkgs.append(name) |
330 | return False | 456 | it = self.contents.iter_next(it) |
457 | return userpkgs, allpkgs | ||
331 | 458 | ||
332 | """ | 459 | def get_build_rep(self): |
333 | Return a list of all selected items which are not -native or -cross | 460 | userpkgs, allpkgs = self.get_selected_packages() |
334 | """ | 461 | image = self.selected_image |
335 | def get_targets(self): | 462 | |
336 | tasks = [] | 463 | return BuildRep(" ".join(userpkgs), " ".join(allpkgs), image) |
337 | 464 | ||
465 | def find_reverse_depends(self, pn): | ||
466 | revdeps = [] | ||
338 | it = self.contents.get_iter_first() | 467 | it = self.contents.get_iter_first() |
468 | |||
339 | while it: | 469 | while it: |
340 | path = self.contents.get_path(it) | 470 | if self.contents.get_value(it, self.COL_DEPS).count(pn) != 0: |
341 | name = self.contents[path][self.COL_NAME] | 471 | revdeps.append(self.contents.get_value(it, self.COL_NAME)) |
342 | stype = self.contents[path][self.COL_TYPE] | ||
343 | if not name.count('-native') and not name.count('-cross'): | ||
344 | tasks.append(name) | ||
345 | it = self.contents.iter_next(it) | 472 | it = self.contents.iter_next(it) |
346 | return tasks | 473 | |
474 | if pn in revdeps: | ||
475 | revdeps.remove(pn) | ||
476 | return revdeps | ||
477 | |||
478 | def set_selected_image(self, img): | ||
479 | self.selected_image = img | ||
480 | path = self.find_path_for_item(img) | ||
481 | self.include_item(item_path=path, | ||
482 | binb="User Selected", | ||
483 | image_contents=True) | ||
484 | |||
485 | self.emit("image-changed", self.selected_image) | ||
486 | |||
487 | def set_selected_packages(self, pkglist): | ||
488 | selected = pkglist | ||
489 | it = self.get_iter_first() | ||
490 | |||
491 | while it: | ||
492 | name = self.get_value(it, self.COL_NAME) | ||
493 | if name in pkglist: | ||
494 | pkglist.remove(name) | ||
495 | path = self.get_path(it) | ||
496 | self.include_item(item_path=path, | ||
497 | binb="User Selected") | ||
498 | if len(pkglist) == 0: | ||
499 | return | ||
500 | it = self.iter_next(it) | ||
501 | |||
502 | def find_image_path(self, image): | ||
503 | it = self.images.get_iter_first() | ||
504 | |||
505 | while it: | ||
506 | image_name = self.images.get_value(it, self.COL_NAME) | ||
507 | if image_name == image: | ||
508 | path = self.images.get_value(it, self.COL_PATH) | ||
509 | meta_pattern = "(\S*)/(meta*/)(\S*)" | ||
510 | meta_match = re.search(meta_pattern, path) | ||
511 | if meta_match: | ||
512 | _, lyr, bbrel = path.partition(meta_match.group(2)) | ||
513 | if bbrel: | ||
514 | path = bbrel | ||
515 | return path | ||
516 | it = self.images.iter_next(it) | ||
diff --git a/bitbake/lib/bb/ui/hob.py b/bitbake/lib/bb/ui/hob.py index 175e5bdf6c..fca41e44dc 100644 --- a/bitbake/lib/bb/ui/hob.py +++ b/bitbake/lib/bb/ui/hob.py | |||
@@ -18,12 +18,16 @@ | |||
18 | # with this program; if not, write to the Free Software Foundation, Inc., | 18 | # with this program; if not, write to the Free Software Foundation, Inc., |
19 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | 19 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
20 | 20 | ||
21 | import glib | ||
21 | import gobject | 22 | import gobject |
22 | import gtk | 23 | import gtk |
23 | from bb.ui.crumbs.progress import ProgressBar | 24 | from bb.ui.crumbs.tasklistmodel import TaskListModel, BuildRep |
24 | from bb.ui.crumbs.tasklistmodel import TaskListModel | ||
25 | from bb.ui.crumbs.hobeventhandler import HobHandler | 25 | from bb.ui.crumbs.hobeventhandler import HobHandler |
26 | from bb.ui.crumbs.configurator import Configurator | ||
27 | from bb.ui.crumbs.hobprefs import HobPrefs | ||
28 | from bb.ui.crumbs.layereditor import LayerEditor | ||
26 | from bb.ui.crumbs.runningbuild import RunningBuildTreeView, RunningBuild | 29 | from bb.ui.crumbs.runningbuild import RunningBuildTreeView, RunningBuild |
30 | from bb.ui.crumbs.hig import CrumbsDialog | ||
27 | import xmlrpclib | 31 | import xmlrpclib |
28 | import logging | 32 | import logging |
29 | import Queue | 33 | import Queue |
@@ -32,226 +36,459 @@ extraCaches = ['bb.cache_extra:HobRecipeInfo'] | |||
32 | 36 | ||
33 | class MainWindow (gtk.Window): | 37 | class MainWindow (gtk.Window): |
34 | 38 | ||
35 | def __init__(self, taskmodel, handler, curr_mach=None, curr_distro=None): | 39 | def __init__(self, taskmodel, handler, configurator, prefs, layers, mach): |
36 | gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL) | 40 | gtk.Window.__init__(self) |
41 | # global state | ||
42 | self.curr_mach = mach | ||
43 | self.machine_handler_id = None | ||
44 | self.image_combo_id = None | ||
45 | self.generating = False | ||
46 | self.files_to_clean = [] | ||
47 | self.selected_image = None | ||
48 | self.selected_packages = None | ||
49 | |||
37 | self.model = taskmodel | 50 | self.model = taskmodel |
38 | self.model.connect("tasklist-populated", self.update_model) | 51 | self.model.connect("tasklist-populated", self.update_model) |
39 | self.curr_mach = curr_mach | 52 | self.model.connect("image-changed", self.image_changed_string_cb) |
40 | self.curr_distro = curr_distro | 53 | self.curr_image_path = None |
41 | self.handler = handler | 54 | self.handler = handler |
42 | self.set_border_width(10) | 55 | self.configurator = configurator |
43 | self.connect("delete-event", gtk.main_quit) | 56 | self.prefs = prefs |
44 | self.set_title("BitBake Image Creator") | 57 | self.layers = layers |
45 | self.set_default_size(700, 600) | 58 | self.save_path = None |
59 | self.dirty = False | ||
60 | |||
61 | self.connect("delete-event", self.destroy_window) | ||
62 | self.set_title("Image Creator") | ||
63 | self.set_icon_name("applications-development") | ||
64 | self.set_default_size(1000, 650) | ||
46 | 65 | ||
47 | self.build = RunningBuild() | 66 | self.build = RunningBuild() |
48 | self.build.connect("build-succeeded", self.running_build_succeeded_cb) | ||
49 | self.build.connect("build-failed", self.running_build_failed_cb) | 67 | self.build.connect("build-failed", self.running_build_failed_cb) |
68 | self.build.connect("build-complete", self.handler.build_complete_cb) | ||
69 | self.build.connect("build-started", self.build_started_cb) | ||
50 | 70 | ||
51 | createview = self.create_build_gui() | 71 | self.handler.connect("build-complete", self.build_complete_cb) |
72 | |||
73 | vbox = gtk.VBox(False, 0) | ||
74 | vbox.set_border_width(0) | ||
75 | vbox.show() | ||
76 | self.add(vbox) | ||
77 | self.menu = self.create_menu() | ||
78 | vbox.pack_start(self.menu, False) | ||
79 | createview = self.create_build_gui() | ||
80 | self.back = None | ||
81 | self.cancel = None | ||
52 | buildview = self.view_build_gui() | 82 | buildview = self.view_build_gui() |
53 | self.nb = gtk.Notebook() | 83 | self.nb = gtk.Notebook() |
54 | self.nb.append_page(createview) | 84 | self.nb.append_page(createview) |
55 | self.nb.append_page(buildview) | 85 | self.nb.append_page(buildview) |
56 | self.nb.set_current_page(0) | 86 | self.nb.set_current_page(0) |
57 | self.nb.set_show_tabs(False) | 87 | self.nb.set_show_tabs(False) |
58 | self.add(self.nb) | 88 | vbox.pack_start(self.nb, expand=True, fill=True) |
59 | self.generating = False | 89 | |
90 | def destroy_window(self, widget, event): | ||
91 | self.quit() | ||
92 | |||
93 | def menu_quit(self, action): | ||
94 | self.quit() | ||
95 | |||
96 | def quit(self): | ||
97 | if self.dirty and len(self.model.contents): | ||
98 | question = "Would you like to save your customisations?" | ||
99 | dialog = CrumbsDialog(self, question, gtk.STOCK_DIALOG_WARNING) | ||
100 | dialog.add_buttons(gtk.STOCK_NO, gtk.RESPONSE_NO, | ||
101 | gtk.STOCK_YES, gtk.RESPONSE_YES) | ||
102 | resp = dialog.run() | ||
103 | if resp == gtk.RESPONSE_YES: | ||
104 | if not self.save_path: | ||
105 | self.get_save_path() | ||
106 | self.save_recipe_file() | ||
107 | rep = self.model.get_build_rep() | ||
108 | rep.writeRecipe(self.save_path, self.model) | ||
109 | |||
110 | gtk.main_quit() | ||
60 | 111 | ||
61 | def scroll_tv_cb(self, model, path, it, view): | 112 | def scroll_tv_cb(self, model, path, it, view): |
62 | view.scroll_to_cell(path) | 113 | view.scroll_to_cell(path) |
63 | 114 | ||
64 | def running_build_failed_cb(self, running_build): | 115 | def running_build_failed_cb(self, running_build): |
65 | # FIXME: handle this | 116 | # FIXME: handle this |
66 | return | 117 | print("Build failed") |
118 | |||
119 | def image_changed_string_cb(self, model, new_image): | ||
120 | cnt = 0 | ||
121 | it = self.model.images.get_iter_first() | ||
122 | while it: | ||
123 | path = self.model.images.get_path(it) | ||
124 | if self.model.images[path][self.model.COL_NAME] == new_image: | ||
125 | self.image_combo.set_active(cnt) | ||
126 | break | ||
127 | it = self.model.images.iter_next(it) | ||
128 | cnt = cnt + 1 | ||
129 | |||
130 | def image_changed_cb(self, combo): | ||
131 | model = self.image_combo.get_model() | ||
132 | it = self.image_combo.get_active_iter() | ||
133 | if it: | ||
134 | path = model.get_path(it) | ||
135 | # Firstly, deselect the previous image | ||
136 | if self.curr_image_path: | ||
137 | self.toggle_package(self.curr_image_path, model) | ||
138 | # Now select the new image and save its path in case we | ||
139 | # change the image later | ||
140 | self.curr_image_path = path | ||
141 | self.toggle_package(path, model, image=True) | ||
142 | |||
143 | def reload_triggered_cb(self, handler, image, packages): | ||
144 | if image: | ||
145 | self.selected_image = image | ||
146 | if len(packages): | ||
147 | self.selected_packages = packages.split() | ||
67 | 148 | ||
68 | def running_build_succeeded_cb(self, running_build): | 149 | def data_generated(self, handler): |
69 | label = gtk.Label("Build completed, start another build?") | 150 | self.generating = False |
70 | dialog = gtk.Dialog("Build complete", | 151 | self.image_combo.set_model(self.model.images_model()) |
71 | self, | 152 | if not self.image_combo_id: |
72 | gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, | 153 | self.image_combo_id = self.image_combo.connect("changed", self.image_changed_cb) |
73 | (gtk.STOCK_NO, gtk.RESPONSE_NO, | 154 | self.enable_widgets() |
74 | gtk.STOCK_YES, gtk.RESPONSE_YES)) | ||
75 | dialog.vbox.pack_start(label) | ||
76 | label.show() | ||
77 | response = dialog.run() | ||
78 | dialog.destroy() | ||
79 | if response == gtk.RESPONSE_YES: | ||
80 | self.model.reset() # NOTE: really? | ||
81 | self.nb.set_current_page(0) | ||
82 | return | ||
83 | 155 | ||
84 | def machine_combo_changed_cb(self, combo, handler): | 156 | def machine_combo_changed_cb(self, combo, handler): |
85 | mach = combo.get_active_text() | 157 | mach = combo.get_active_text() |
86 | if mach != self.curr_mach: | 158 | if mach != self.curr_mach: |
87 | self.curr_mach = mach | 159 | self.curr_mach = mach |
160 | # Flush this straight to the file as MACHINE is changed | ||
161 | # independently of other 'Preferences' | ||
162 | self.configurator.setLocalConfVar('MACHINE', mach) | ||
163 | self.configurator.writeLocalConf() | ||
88 | handler.set_machine(mach) | 164 | handler.set_machine(mach) |
165 | handler.reload_data() | ||
89 | 166 | ||
90 | def update_machines(self, handler, machines): | 167 | def update_machines(self, handler, machines): |
91 | active = 0 | 168 | active = 0 |
92 | for machine in machines: | 169 | # disconnect the signal handler before updating the combo model |
93 | self.machine_combo.append_text(machine) | 170 | if self.machine_handler_id: |
94 | if machine == self.curr_mach: | 171 | self.machine_combo.disconnect(self.machine_handler_id) |
172 | self.machine_handler_id = None | ||
173 | |||
174 | model = self.machine_combo.get_model() | ||
175 | if model: | ||
176 | model.clear() | ||
177 | |||
178 | for machine in machines: | ||
179 | self.machine_combo.append_text(machine) | ||
180 | if machine == self.curr_mach: | ||
95 | self.machine_combo.set_active(active) | 181 | self.machine_combo.set_active(active) |
96 | active = active + 1 | 182 | active = active + 1 |
97 | self.machine_combo.connect("changed", self.machine_combo_changed_cb, handler) | 183 | |
98 | 184 | self.machine_handler_id = self.machine_combo.connect("changed", self.machine_combo_changed_cb, handler) | |
99 | def update_distros(self, handler, distros): | 185 | |
100 | # FIXME: when we add UI for changing distro this will be used | 186 | def set_busy_cursor(self, busy=True): |
101 | return | 187 | """ |
102 | 188 | Convenience method to set the cursor to a spinner when executing | |
103 | def data_generated(self, handler): | 189 | a potentially lengthy process. |
104 | self.generating = False | 190 | A busy value of False will set the cursor back to the default |
105 | 191 | left pointer. | |
106 | def spin_idle_func(self, pbar): | 192 | """ |
193 | if busy: | ||
194 | cursor = gtk.gdk.Cursor(gtk.gdk.WATCH) | ||
195 | else: | ||
196 | # TODO: presumably the default cursor is different on RTL | ||
197 | # systems. Can we determine the default cursor? Or at least | ||
198 | # the cursor which is set before we change it? | ||
199 | cursor = gtk.gdk.Cursor(gtk.gdk.LEFT_PTR) | ||
200 | window = self.get_root_window() | ||
201 | window.set_cursor(cursor) | ||
202 | |||
203 | def busy_idle_func(self): | ||
107 | if self.generating: | 204 | if self.generating: |
108 | pbar.pulse() | 205 | self.progress.set_text("Loading...") |
206 | self.progress.pulse() | ||
109 | return True | 207 | return True |
110 | else: | 208 | else: |
111 | pbar.hide() | 209 | if not self.image_combo_id: |
210 | self.image_combo_id = self.image_combo.connect("changed", self.image_changed_cb) | ||
211 | self.progress.set_text("Loaded") | ||
212 | self.progress.set_fraction(0.0) | ||
213 | self.set_busy_cursor(False) | ||
112 | return False | 214 | return False |
113 | 215 | ||
114 | def busy(self, handler): | 216 | def busy(self, handler): |
115 | self.generating = True | 217 | self.generating = True |
116 | pbar = ProgressBar(self) | 218 | self.set_busy_cursor() |
117 | pbar.connect("delete-event", gtk.main_quit) # NOTE: questionable... | 219 | if self.image_combo_id: |
118 | pbar.pulse() | 220 | self.image_combo.disconnect(self.image_combo_id) |
119 | gobject.timeout_add (200, | 221 | self.image_combo_id = None |
120 | self.spin_idle_func, | 222 | self.progress.pulse() |
121 | pbar) | 223 | gobject.timeout_add (200, self.busy_idle_func) |
224 | self.disable_widgets() | ||
225 | |||
226 | def enable_widgets(self): | ||
227 | self.menu.set_sensitive(True) | ||
228 | self.machine_combo.set_sensitive(True) | ||
229 | self.image_combo.set_sensitive(True) | ||
230 | self.nb.set_sensitive(True) | ||
231 | self.contents_tree.set_sensitive(True) | ||
232 | |||
233 | def disable_widgets(self): | ||
234 | self.menu.set_sensitive(False) | ||
235 | self.machine_combo.set_sensitive(False) | ||
236 | self.image_combo.set_sensitive(False) | ||
237 | self.nb.set_sensitive(False) | ||
238 | self.contents_tree.set_sensitive(False) | ||
122 | 239 | ||
123 | def update_model(self, model): | 240 | def update_model(self, model): |
124 | pkgsaz_model = gtk.TreeModelSort(self.model.packages_model()) | 241 | # We want the packages model to be alphabetised and sortable so create |
242 | # a TreeModelSort to use in the view | ||
243 | pkgsaz_model = gtk.TreeModelSort(self.model.packages_model()) | ||
125 | pkgsaz_model.set_sort_column_id(self.model.COL_NAME, gtk.SORT_ASCENDING) | 244 | pkgsaz_model.set_sort_column_id(self.model.COL_NAME, gtk.SORT_ASCENDING) |
245 | # Unset default sort func so that we only toggle between A-Z and | ||
246 | # Z-A sorting | ||
247 | pkgsaz_model.set_default_sort_func(None) | ||
126 | self.pkgsaz_tree.set_model(pkgsaz_model) | 248 | self.pkgsaz_tree.set_model(pkgsaz_model) |
127 | 249 | ||
128 | # FIXME: need to implement a custom sort function, as otherwise the column | 250 | # We want the contents to be alphabetised so create a TreeModelSort to |
129 | # is re-ordered when toggling the inclusion state (COL_INC) | 251 | # use in the view |
130 | pkgsgrp_model = gtk.TreeModelSort(self.model.packages_model()) | 252 | contents_model = gtk.TreeModelSort(self.model.contents_model()) |
131 | pkgsgrp_model.set_sort_column_id(self.model.COL_GROUP, gtk.SORT_ASCENDING) | 253 | contents_model.set_sort_column_id(self.model.COL_NAME, gtk.SORT_ASCENDING) |
132 | self.pkgsgrp_tree.set_model(pkgsgrp_model) | 254 | # Unset default sort func so that we only toggle between A-Z and |
133 | 255 | # Z-A sorting | |
134 | self.contents_tree.set_model(self.model.contents_model()) | 256 | contents_model.set_default_sort_func(None) |
135 | self.images_tree.set_model(self.model.images_model()) | 257 | self.contents_tree.set_model(contents_model) |
136 | self.tasks_tree.set_model(self.model.tasks_model()) | 258 | self.tasks_tree.set_model(self.model.tasks_model()) |
259 | |||
260 | if self.selected_image: | ||
261 | if self.image_combo_id: | ||
262 | self.image_combo.disconnect(self.image_combo_id) | ||
263 | self.image_combo_id = None | ||
264 | self.model.set_selected_image(self.selected_image) | ||
265 | self.selected_image = None | ||
266 | if not self.image_combo_id: | ||
267 | self.image_combo_id = self.image_combo.connect("changed", self.image_changed_cb) | ||
268 | |||
269 | if self.selected_packages: | ||
270 | self.model.set_selected_packages(self.selected_packages) | ||
271 | self.selected_packages = None | ||
137 | 272 | ||
138 | def reset_clicked_cb(self, button): | 273 | def reset_clicked_cb(self, button): |
139 | label = gtk.Label("Are you sure you want to reset the image contents?") | 274 | lbl = "<b>Reset your selections?</b>\n\nAny new changes you have made will be lost" |
140 | dialog = gtk.Dialog("Confirm reset", self, | 275 | dialog = CrumbsDialog(self, lbl, gtk.STOCK_DIALOG_WARNING) |
141 | gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, | 276 | dialog.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL) |
142 | (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, | 277 | dialog.add_button("Reset", gtk.RESPONSE_OK) |
143 | gtk.STOCK_OK, gtk.RESPONSE_ACCEPT)) | ||
144 | dialog.vbox.pack_start(label) | ||
145 | label.show() | ||
146 | response = dialog.run() | 278 | response = dialog.run() |
147 | dialog.destroy() | 279 | dialog.destroy() |
148 | if (response == gtk.RESPONSE_ACCEPT): | 280 | if response == gtk.RESPONSE_OK: |
149 | self.model.reset() | 281 | self.reset_build() |
150 | return | 282 | return |
151 | 283 | ||
284 | def reset_build(self): | ||
285 | self.image_combo.disconnect(self.image_combo_id) | ||
286 | self.image_combo_id = None | ||
287 | self.image_combo.set_active(-1) | ||
288 | self.image_combo_id = self.image_combo.connect("changed", self.image_changed_cb) | ||
289 | self.model.reset() | ||
290 | |||
291 | def layers_cb(self, action): | ||
292 | resp = self.layers.run() | ||
293 | self.layers.save_current_layers() | ||
294 | self.layers.hide() | ||
295 | |||
296 | def add_layer_cb(self, action): | ||
297 | self.layers.find_layer(self) | ||
298 | |||
299 | def preferences_cb(self, action): | ||
300 | resp = self.prefs.run() | ||
301 | self.prefs.write_changes() | ||
302 | self.prefs.hide() | ||
303 | |||
304 | def about_cb(self, action): | ||
305 | about = gtk.AboutDialog() | ||
306 | about.set_name("Image Creator") | ||
307 | about.set_copyright("Copyright (C) 2011 Intel Corporation") | ||
308 | about.set_authors(["Joshua Lock <josh@linux.intel.com>"]) | ||
309 | about.set_logo_icon_name("applications-development") | ||
310 | about.run() | ||
311 | about.destroy() | ||
312 | |||
313 | def save_recipe_file(self): | ||
314 | rep = self.model.get_build_rep() | ||
315 | rep.writeRecipe(self.save_path, self.model) | ||
316 | self.dirty = False | ||
317 | |||
318 | def get_save_path(self): | ||
319 | chooser = gtk.FileChooserDialog(title=None, parent=self, | ||
320 | action=gtk.FILE_CHOOSER_ACTION_SAVE, | ||
321 | buttons=(gtk.STOCK_CANCEL, | ||
322 | gtk.RESPONSE_CANCEL, | ||
323 | gtk.STOCK_SAVE, | ||
324 | gtk.RESPONSE_OK,)) | ||
325 | chooser.set_current_name("myimage.bb") | ||
326 | response = chooser.run() | ||
327 | if response == gtk.RESPONSE_OK: | ||
328 | self.save_path = chooser.get_filename() | ||
329 | chooser.destroy() | ||
330 | |||
331 | def save_cb(self, action): | ||
332 | if not self.save_path: | ||
333 | self.get_save_path() | ||
334 | self.save_recipe_file() | ||
335 | |||
336 | def save_as_cb(self, action): | ||
337 | self.get_save_path() | ||
338 | self.save_recipe_file() | ||
339 | |||
340 | def open_cb(self, action): | ||
341 | chooser = gtk.FileChooserDialog(title=None, parent=self, | ||
342 | action=gtk.FILE_CHOOSER_ACTION_OPEN, | ||
343 | buttons=(gtk.STOCK_CANCEL, | ||
344 | gtk.RESPONSE_CANCEL, | ||
345 | gtk.STOCK_OPEN, | ||
346 | gtk.RESPONSE_OK)) | ||
347 | response = chooser.run() | ||
348 | rep = BuildRep(None, None, None) | ||
349 | if response == gtk.RESPONSE_OK: | ||
350 | rep.loadRecipe(chooser.get_filename()) | ||
351 | chooser.destroy() | ||
352 | self.model.load_image_rep(rep) | ||
353 | self.dirty = False | ||
354 | |||
152 | def bake_clicked_cb(self, button): | 355 | def bake_clicked_cb(self, button): |
153 | if not self.model.targets_contains_image(): | 356 | rep = self.model.get_build_rep() |
154 | label = gtk.Label("No image was selected. Just build the selected packages?") | 357 | if not rep.base_image: |
155 | dialog = gtk.Dialog("Warning, no image selected", | 358 | lbl = "<b>Build only packages?</b>\n\nAn image has not been selected, so only the selected packages will be built." |
156 | self, | 359 | dialog = CrumbsDialog(self, lbl, gtk.STOCK_DIALOG_WARNING) |
157 | gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, | 360 | dialog.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL) |
158 | (gtk.STOCK_NO, gtk.RESPONSE_NO, | 361 | dialog.add_button("Build", gtk.RESPONSE_YES) |
159 | gtk.STOCK_YES, gtk.RESPONSE_YES)) | ||
160 | dialog.vbox.pack_start(label) | ||
161 | label.show() | ||
162 | response = dialog.run() | 362 | response = dialog.run() |
163 | dialog.destroy() | 363 | dialog.destroy() |
164 | if not response == gtk.RESPONSE_YES: | 364 | if response == gtk.RESPONSE_CANCEL: |
165 | return | 365 | return |
166 | |||
167 | # Note: We could "squash" the targets list to only include things not brought in by an image | ||
168 | task_list = self.model.get_targets() | ||
169 | if len(task_list): | ||
170 | tasks = " ".join(task_list) | ||
171 | # TODO: show a confirmation dialog | ||
172 | print("Including these extra tasks in IMAGE_INSTALL: %s" % tasks) | ||
173 | else: | 366 | else: |
174 | return | 367 | # TODO: show a confirmation dialog ? |
175 | 368 | if not self.save_path: | |
369 | import tempfile, datetime | ||
370 | image_name = "hob-%s-variant-%s.bb" % (rep.base_image, datetime.date.today().isoformat()) | ||
371 | image_dir = os.path.join(tempfile.gettempdir(), 'hob-images') | ||
372 | bb.utils.mkdirhier(image_dir) | ||
373 | recipepath = os.path.join(image_dir, image_name) | ||
374 | else: | ||
375 | recipepath = self.save_path | ||
376 | |||
377 | rep.writeRecipe(recipepath, self.model) | ||
378 | # In the case where we saved the file for the purpose of building | ||
379 | # it we should then delete it so that the users workspace doesn't | ||
380 | # contain files they haven't explicitly saved there. | ||
381 | if not self.save_path: | ||
382 | self.files_to_clean.append(recipepath) | ||
383 | |||
384 | self.handler.queue_image_recipe_path(recipepath) | ||
385 | |||
386 | self.handler.build_packages(rep.allpkgs.split(" ")) | ||
176 | self.nb.set_current_page(1) | 387 | self.nb.set_current_page(1) |
177 | self.handler.run_build(task_list) | ||
178 | 388 | ||
179 | return | 389 | def back_button_clicked_cb(self, button): |
390 | self.toggle_createview() | ||
180 | 391 | ||
181 | def advanced_expander_cb(self, expander, param): | 392 | def toggle_createview(self): |
182 | return | 393 | self.build.model.clear() |
183 | 394 | self.nb.set_current_page(0) | |
184 | def images(self): | ||
185 | self.images_tree = gtk.TreeView() | ||
186 | self.images_tree.set_headers_visible(True) | ||
187 | self.images_tree.set_headers_clickable(False) | ||
188 | self.images_tree.set_enable_search(True) | ||
189 | self.images_tree.set_search_column(0) | ||
190 | self.images_tree.get_selection().set_mode(gtk.SELECTION_NONE) | ||
191 | 395 | ||
192 | col = gtk.TreeViewColumn('Package') | 396 | def build_complete_cb(self, running_build): |
193 | col1 = gtk.TreeViewColumn('Description') | 397 | self.back.connect("clicked", self.back_button_clicked_cb) |
194 | col2 = gtk.TreeViewColumn('License') | 398 | self.back.set_sensitive(True) |
195 | col3 = gtk.TreeViewColumn('Include') | 399 | self.cancel.set_sensitive(False) |
196 | col3.set_resizable(False) | 400 | for f in self.files_to_clean: |
401 | os.remove(f) | ||
197 | 402 | ||
198 | self.images_tree.append_column(col) | 403 | lbl = "<b>Build completed</b>\n\nClick 'Edit Image' to start another build or 'View Log' to view the build log." |
199 | self.images_tree.append_column(col1) | 404 | if self.handler.building == "image": |
200 | self.images_tree.append_column(col2) | 405 | deploy = self.handler.get_image_deploy_dir() |
201 | self.images_tree.append_column(col3) | 406 | lbl = lbl + "\n<a href=\"file://%s\" title=\"%s\">Browse folder of built images</a>." % (deploy, deploy) |
202 | 407 | ||
203 | cell = gtk.CellRendererText() | 408 | dialog = CrumbsDialog(self, lbl) |
204 | cell1 = gtk.CellRendererText() | 409 | dialog.add_button("View Log", gtk.RESPONSE_CANCEL) |
205 | cell2 = gtk.CellRendererText() | 410 | dialog.add_button("Edit Image", gtk.RESPONSE_OK) |
206 | cell3 = gtk.CellRendererToggle() | 411 | response = dialog.run() |
207 | cell3.set_property('activatable', True) | 412 | dialog.destroy() |
208 | cell3.connect("toggled", self.toggle_include_cb, self.images_tree) | 413 | if response == gtk.RESPONSE_OK: |
209 | 414 | self.toggle_createview() | |
210 | col.pack_start(cell, True) | 415 | |
211 | col1.pack_start(cell1, True) | 416 | def build_started_cb(self, running_build): |
212 | col2.pack_start(cell2, True) | 417 | self.back.set_sensitive(False) |
213 | col3.pack_start(cell3, True) | 418 | self.cancel.set_sensitive(True) |
214 | 419 | ||
215 | col.set_attributes(cell, text=self.model.COL_NAME) | 420 | def include_gplv3_cb(self, toggle): |
216 | col1.set_attributes(cell1, text=self.model.COL_DESC) | 421 | excluded = toggle.get_active() |
217 | col2.set_attributes(cell2, text=self.model.COL_LIC) | 422 | self.handler.toggle_gplv3(excluded) |
218 | col3.set_attributes(cell3, active=self.model.COL_INC) | 423 | |
219 | 424 | def change_bb_threads(self, spinner): | |
220 | self.images_tree.show() | 425 | val = spinner.get_value_as_int() |
221 | 426 | self.handler.set_bbthreads(val) | |
222 | scroll = gtk.ScrolledWindow() | 427 | |
223 | scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS) | 428 | def change_make_threads(self, spinner): |
224 | scroll.set_shadow_type(gtk.SHADOW_IN) | 429 | val = spinner.get_value_as_int() |
225 | scroll.add(self.images_tree) | 430 | self.handler.set_pmake(val) |
226 | 431 | ||
227 | return scroll | 432 | def toggle_toolchain(self, check): |
433 | enabled = check.get_active() | ||
434 | self.handler.toggle_toolchain(enabled) | ||
435 | |||
436 | def toggle_headers(self, check): | ||
437 | enabled = check.get_active() | ||
438 | self.handler.toggle_toolchain_headers(enabled) | ||
439 | |||
440 | def toggle_package_idle_cb(self, opath, image): | ||
441 | """ | ||
442 | As the operations which we're calling on the model can take | ||
443 | a significant amount of time (in the order of seconds) during which | ||
444 | the GUI is unresponsive as the main loop is blocked perform them in | ||
445 | an idle function which at least enables us to set the busy cursor | ||
446 | before the UI is blocked giving the appearance of being responsive. | ||
447 | """ | ||
448 | # Whether the item is currently included | ||
449 | inc = self.model[opath][self.model.COL_INC] | ||
450 | # If the item is already included, mark it for removal then | ||
451 | # the sweep_up() method finds affected items and marks them | ||
452 | # appropriately | ||
453 | if inc: | ||
454 | self.model.mark(opath) | ||
455 | self.model.sweep_up() | ||
456 | # If the item isn't included, mark it for inclusion | ||
457 | else: | ||
458 | self.model.include_item(item_path=opath, | ||
459 | binb="User Selected", | ||
460 | image_contents=image) | ||
461 | |||
462 | self.set_busy_cursor(False) | ||
463 | return False | ||
464 | |||
465 | def toggle_package(self, path, model, image=False): | ||
466 | # Warn user before removing packages | ||
467 | inc = model[path][self.model.COL_INC] | ||
468 | if inc: | ||
469 | pn = model[path][self.model.COL_NAME] | ||
470 | revdeps = self.model.find_reverse_depends(pn) | ||
471 | if len(revdeps): | ||
472 | lbl = "<b>Remove %s?</b>\n\nThis action cannot be undone and all packages which depend on this will be removed\nPackages which depend on %s include %s." % (pn, pn, ", ".join(revdeps).rstrip(",")) | ||
473 | else: | ||
474 | lbl = "<b>Remove %s?</b>\n\nThis action cannot be undone." % pn | ||
475 | dialog = CrumbsDialog(self, lbl, gtk.STOCK_DIALOG_WARNING) | ||
476 | dialog.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL) | ||
477 | dialog.add_button("Remove", gtk.RESPONSE_OK) | ||
478 | response = dialog.run() | ||
479 | dialog.destroy() | ||
480 | if response == gtk.RESPONSE_CANCEL: | ||
481 | return | ||
228 | 482 | ||
229 | def toggle_package(self, path, model): | 483 | self.set_busy_cursor() |
230 | # Convert path to path in original model | 484 | # Convert path to path in original model |
231 | opath = model.convert_path_to_child_path(path) | 485 | opath = model.convert_path_to_child_path(path) |
232 | # current include status | 486 | # This is a potentially length call which can block the |
233 | inc = self.model[opath][self.model.COL_INC] | 487 | # main loop, therefore do the work in an idle func to keep |
234 | if inc: | 488 | # the UI responsive |
235 | self.model.mark(opath) | 489 | glib.idle_add(self.toggle_package_idle_cb, opath, image) |
236 | self.model.sweep_up() | ||
237 | #self.model.remove_package_full(cpath) | ||
238 | else: | ||
239 | self.model.include_item(opath) | ||
240 | return | ||
241 | 490 | ||
242 | def remove_package_cb(self, cell, path): | 491 | self.dirty = True |
243 | model = self.model.contents_model() | ||
244 | label = gtk.Label("Are you sure you want to remove this item?") | ||
245 | dialog = gtk.Dialog("Confirm removal", self, | ||
246 | gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, | ||
247 | (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, | ||
248 | gtk.STOCK_OK, gtk.RESPONSE_ACCEPT)) | ||
249 | dialog.vbox.pack_start(label) | ||
250 | label.show() | ||
251 | response = dialog.run() | ||
252 | dialog.destroy() | ||
253 | if (response == gtk.RESPONSE_ACCEPT): | ||
254 | self.toggle_package(path, model) | ||
255 | 492 | ||
256 | def toggle_include_cb(self, cell, path, tv): | 493 | def toggle_include_cb(self, cell, path, tv): |
257 | model = tv.get_model() | 494 | model = tv.get_model() |
@@ -262,23 +499,36 @@ class MainWindow (gtk.Window): | |||
262 | sort_model = tv.get_model() | 499 | sort_model = tv.get_model() |
263 | cpath = sort_model.convert_path_to_child_path(path) | 500 | cpath = sort_model.convert_path_to_child_path(path) |
264 | self.toggle_package(cpath, sort_model.get_model()) | 501 | self.toggle_package(cpath, sort_model.get_model()) |
265 | 502 | ||
266 | def pkgsaz(self): | 503 | def pkgsaz(self): |
504 | vbox = gtk.VBox(False, 6) | ||
505 | vbox.show() | ||
267 | self.pkgsaz_tree = gtk.TreeView() | 506 | self.pkgsaz_tree = gtk.TreeView() |
268 | self.pkgsaz_tree.set_headers_visible(True) | 507 | self.pkgsaz_tree.set_headers_visible(True) |
269 | self.pkgsaz_tree.set_headers_clickable(True) | 508 | self.pkgsaz_tree.set_headers_clickable(True) |
270 | self.pkgsaz_tree.set_enable_search(True) | 509 | self.pkgsaz_tree.set_enable_search(True) |
271 | self.pkgsaz_tree.set_search_column(0) | 510 | self.pkgsaz_tree.set_search_column(0) |
272 | self.pkgsaz_tree.get_selection().set_mode(gtk.SELECTION_NONE) | 511 | self.pkgsaz_tree.get_selection().set_mode(gtk.SELECTION_SINGLE) |
273 | 512 | ||
274 | col = gtk.TreeViewColumn('Package') | 513 | col = gtk.TreeViewColumn('Package') |
514 | col.set_clickable(True) | ||
515 | col.set_sort_column_id(self.model.COL_NAME) | ||
516 | col.set_min_width(220) | ||
275 | col1 = gtk.TreeViewColumn('Description') | 517 | col1 = gtk.TreeViewColumn('Description') |
276 | col1.set_resizable(True) | 518 | col1.set_resizable(True) |
519 | col1.set_min_width(360) | ||
277 | col2 = gtk.TreeViewColumn('License') | 520 | col2 = gtk.TreeViewColumn('License') |
278 | col2.set_resizable(True) | 521 | col2.set_resizable(True) |
522 | col2.set_clickable(True) | ||
523 | col2.set_sort_column_id(self.model.COL_LIC) | ||
524 | col2.set_min_width(170) | ||
279 | col3 = gtk.TreeViewColumn('Group') | 525 | col3 = gtk.TreeViewColumn('Group') |
280 | col4 = gtk.TreeViewColumn('Include') | 526 | col3.set_clickable(True) |
281 | col4.set_resizable(False) | 527 | col3.set_sort_column_id(self.model.COL_GROUP) |
528 | col4 = gtk.TreeViewColumn('Included') | ||
529 | col4.set_min_width(80) | ||
530 | col4.set_max_width(90) | ||
531 | col4.set_sort_column_id(self.model.COL_INC) | ||
282 | 532 | ||
283 | self.pkgsaz_tree.append_column(col) | 533 | self.pkgsaz_tree.append_column(col) |
284 | self.pkgsaz_tree.append_column(col1) | 534 | self.pkgsaz_tree.append_column(col1) |
@@ -288,9 +538,9 @@ class MainWindow (gtk.Window): | |||
288 | 538 | ||
289 | cell = gtk.CellRendererText() | 539 | cell = gtk.CellRendererText() |
290 | cell1 = gtk.CellRendererText() | 540 | cell1 = gtk.CellRendererText() |
291 | cell1.set_property('width-chars', 20) | 541 | cell1.set_property('width-chars', 20) |
292 | cell2 = gtk.CellRendererText() | 542 | cell2 = gtk.CellRendererText() |
293 | cell2.set_property('width-chars', 20) | 543 | cell2.set_property('width-chars', 20) |
294 | cell3 = gtk.CellRendererText() | 544 | cell3 = gtk.CellRendererText() |
295 | cell4 = gtk.CellRendererToggle() | 545 | cell4 = gtk.CellRendererToggle() |
296 | cell4.set_property('activatable', True) | 546 | cell4.set_property('activatable', True) |
@@ -300,7 +550,7 @@ class MainWindow (gtk.Window): | |||
300 | col1.pack_start(cell1, True) | 550 | col1.pack_start(cell1, True) |
301 | col2.pack_start(cell2, True) | 551 | col2.pack_start(cell2, True) |
302 | col3.pack_start(cell3, True) | 552 | col3.pack_start(cell3, True) |
303 | col4.pack_start(cell4, True) | 553 | col4.pack_end(cell4, True) |
304 | 554 | ||
305 | col.set_attributes(cell, text=self.model.COL_NAME) | 555 | col.set_attributes(cell, text=self.model.COL_NAME) |
306 | col1.set_attributes(cell1, text=self.model.COL_DESC) | 556 | col1.set_attributes(cell1, text=self.model.COL_DESC) |
@@ -314,75 +564,43 @@ class MainWindow (gtk.Window): | |||
314 | scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS) | 564 | scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS) |
315 | scroll.set_shadow_type(gtk.SHADOW_IN) | 565 | scroll.set_shadow_type(gtk.SHADOW_IN) |
316 | scroll.add(self.pkgsaz_tree) | 566 | scroll.add(self.pkgsaz_tree) |
567 | vbox.pack_start(scroll, True, True, 0) | ||
568 | |||
569 | hb = gtk.HBox(False, 0) | ||
570 | hb.show() | ||
571 | search = gtk.Entry() | ||
572 | search.set_icon_from_stock(gtk.ENTRY_ICON_SECONDARY, "gtk-clear") | ||
573 | search.connect("icon-release", self.search_entry_clear_cb) | ||
574 | search.show() | ||
575 | self.pkgsaz_tree.set_search_entry(search) | ||
576 | hb.pack_end(search, False, False, 0) | ||
577 | label = gtk.Label("Search packages:") | ||
578 | label.show() | ||
579 | hb.pack_end(label, False, False, 6) | ||
580 | vbox.pack_start(hb, False, False, 0) | ||
317 | 581 | ||
318 | return scroll | 582 | return vbox |
319 | |||
320 | def pkgsgrp(self): | ||
321 | self.pkgsgrp_tree = gtk.TreeView() | ||
322 | self.pkgsgrp_tree.set_headers_visible(True) | ||
323 | self.pkgsgrp_tree.set_headers_clickable(False) | ||
324 | self.pkgsgrp_tree.set_enable_search(True) | ||
325 | self.pkgsgrp_tree.set_search_column(0) | ||
326 | self.pkgsgrp_tree.get_selection().set_mode(gtk.SELECTION_NONE) | ||
327 | |||
328 | col = gtk.TreeViewColumn('Package') | ||
329 | col1 = gtk.TreeViewColumn('Description') | ||
330 | col1.set_resizable(True) | ||
331 | col2 = gtk.TreeViewColumn('License') | ||
332 | col2.set_resizable(True) | ||
333 | col3 = gtk.TreeViewColumn('Group') | ||
334 | col4 = gtk.TreeViewColumn('Include') | ||
335 | col4.set_resizable(False) | ||
336 | |||
337 | self.pkgsgrp_tree.append_column(col) | ||
338 | self.pkgsgrp_tree.append_column(col1) | ||
339 | self.pkgsgrp_tree.append_column(col2) | ||
340 | self.pkgsgrp_tree.append_column(col3) | ||
341 | self.pkgsgrp_tree.append_column(col4) | ||
342 | |||
343 | cell = gtk.CellRendererText() | ||
344 | cell1 = gtk.CellRendererText() | ||
345 | cell1.set_property('width-chars', 20) | ||
346 | cell2 = gtk.CellRendererText() | ||
347 | cell2.set_property('width-chars', 20) | ||
348 | cell3 = gtk.CellRendererText() | ||
349 | cell4 = gtk.CellRendererToggle() | ||
350 | cell4.set_property("activatable", True) | ||
351 | cell4.connect("toggled", self.toggle_pkg_include_cb, self.pkgsgrp_tree) | ||
352 | |||
353 | col.pack_start(cell, True) | ||
354 | col1.pack_start(cell1, True) | ||
355 | col2.pack_start(cell2, True) | ||
356 | col3.pack_start(cell3, True) | ||
357 | col4.pack_start(cell4, True) | ||
358 | |||
359 | col.set_attributes(cell, text=self.model.COL_NAME) | ||
360 | col1.set_attributes(cell1, text=self.model.COL_DESC) | ||
361 | col2.set_attributes(cell2, text=self.model.COL_LIC) | ||
362 | col3.set_attributes(cell3, text=self.model.COL_GROUP) | ||
363 | col4.set_attributes(cell4, active=self.model.COL_INC) | ||
364 | |||
365 | self.pkgsgrp_tree.show() | ||
366 | |||
367 | scroll = gtk.ScrolledWindow() | ||
368 | scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS) | ||
369 | scroll.set_shadow_type(gtk.SHADOW_IN) | ||
370 | scroll.add(self.pkgsgrp_tree) | ||
371 | 583 | ||
372 | return scroll | 584 | def search_entry_clear_cb(self, entry, icon_pos, event): |
585 | entry.set_text("") | ||
373 | 586 | ||
374 | def tasks(self): | 587 | def tasks(self): |
588 | vbox = gtk.VBox(False, 6) | ||
589 | vbox.show() | ||
375 | self.tasks_tree = gtk.TreeView() | 590 | self.tasks_tree = gtk.TreeView() |
376 | self.tasks_tree.set_headers_visible(True) | 591 | self.tasks_tree.set_headers_visible(True) |
377 | self.tasks_tree.set_headers_clickable(False) | 592 | self.tasks_tree.set_headers_clickable(False) |
378 | self.tasks_tree.set_enable_search(True) | 593 | self.tasks_tree.set_enable_search(True) |
379 | self.tasks_tree.set_search_column(0) | 594 | self.tasks_tree.set_search_column(0) |
380 | self.tasks_tree.get_selection().set_mode(gtk.SELECTION_NONE) | 595 | self.tasks_tree.get_selection().set_mode(gtk.SELECTION_SINGLE) |
381 | 596 | ||
382 | col = gtk.TreeViewColumn('Package') | 597 | col = gtk.TreeViewColumn('Package') |
598 | col.set_min_width(430) | ||
383 | col1 = gtk.TreeViewColumn('Description') | 599 | col1 = gtk.TreeViewColumn('Description') |
600 | col1.set_min_width(430) | ||
384 | col2 = gtk.TreeViewColumn('Include') | 601 | col2 = gtk.TreeViewColumn('Include') |
385 | col2.set_resizable(False) | 602 | col2.set_min_width(70) |
603 | col2.set_max_width(80) | ||
386 | 604 | ||
387 | self.tasks_tree.append_column(col) | 605 | self.tasks_tree.append_column(col) |
388 | self.tasks_tree.append_column(col1) | 606 | self.tasks_tree.append_column(col1) |
@@ -396,7 +614,7 @@ class MainWindow (gtk.Window): | |||
396 | 614 | ||
397 | col.pack_start(cell, True) | 615 | col.pack_start(cell, True) |
398 | col1.pack_start(cell1, True) | 616 | col1.pack_start(cell1, True) |
399 | col2.pack_start(cell2, True) | 617 | col2.pack_end(cell2, True) |
400 | 618 | ||
401 | col.set_attributes(cell, text=self.model.COL_NAME) | 619 | col.set_attributes(cell, text=self.model.COL_NAME) |
402 | col1.set_attributes(cell1, text=self.model.COL_DESC) | 620 | col1.set_attributes(cell1, text=self.model.COL_DESC) |
@@ -408,26 +626,37 @@ class MainWindow (gtk.Window): | |||
408 | scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS) | 626 | scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS) |
409 | scroll.set_shadow_type(gtk.SHADOW_IN) | 627 | scroll.set_shadow_type(gtk.SHADOW_IN) |
410 | scroll.add(self.tasks_tree) | 628 | scroll.add(self.tasks_tree) |
629 | vbox.pack_start(scroll, True, True, 0) | ||
630 | |||
631 | hb = gtk.HBox(False, 0) | ||
632 | hb.show() | ||
633 | search = gtk.Entry() | ||
634 | search.show() | ||
635 | self.tasks_tree.set_search_entry(search) | ||
636 | hb.pack_end(search, False, False, 0) | ||
637 | label = gtk.Label("Search collections:") | ||
638 | label.show() | ||
639 | hb.pack_end(label, False, False, 6) | ||
640 | vbox.pack_start(hb, False, False, 0) | ||
411 | 641 | ||
412 | return scroll | 642 | return vbox |
413 | 643 | ||
414 | def cancel_build(self, button): | 644 | def cancel_build(self, button): |
415 | label = gtk.Label("Do you really want to stop this build?") | 645 | lbl = "<b>Stop build?</b>\n\nAre you sure you want to stop this build?" |
416 | dialog = gtk.Dialog("Cancel build", | 646 | dialog = CrumbsDialog(self, lbl, gtk.STOCK_DIALOG_WARNING) |
417 | self, | 647 | dialog.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL) |
418 | gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, | 648 | dialog.add_button("Stop", gtk.RESPONSE_OK) |
419 | (gtk.STOCK_NO, gtk.RESPONSE_NO, | 649 | dialog.add_button("Force Stop", gtk.RESPONSE_YES) |
420 | gtk.STOCK_YES, gtk.RESPONSE_YES)) | ||
421 | dialog.vbox.pack_start(label) | ||
422 | label.show() | ||
423 | response = dialog.run() | 650 | response = dialog.run() |
424 | dialog.destroy() | 651 | dialog.destroy() |
425 | if response == gtk.RESPONSE_YES: | 652 | if response == gtk.RESPONSE_OK: |
426 | self.handler.cancel_build() | 653 | self.handler.cancel_build() |
427 | return | 654 | elif response == gtk.RESPONSE_YES: |
655 | self.handler.cancel_build(True) | ||
428 | 656 | ||
429 | def view_build_gui(self): | 657 | def view_build_gui(self): |
430 | vbox = gtk.VBox(False, 6) | 658 | vbox = gtk.VBox(False, 12) |
659 | vbox.set_border_width(6) | ||
431 | vbox.show() | 660 | vbox.show() |
432 | build_tv = RunningBuildTreeView() | 661 | build_tv = RunningBuildTreeView() |
433 | build_tv.show() | 662 | build_tv.show() |
@@ -438,20 +667,74 @@ class MainWindow (gtk.Window): | |||
438 | scrolled_view.add(build_tv) | 667 | scrolled_view.add(build_tv) |
439 | scrolled_view.show() | 668 | scrolled_view.show() |
440 | vbox.pack_start(scrolled_view, expand=True, fill=True) | 669 | vbox.pack_start(scrolled_view, expand=True, fill=True) |
441 | hbox = gtk.HBox(False, 6) | 670 | hbox = gtk.HBox(False, 12) |
442 | hbox.show() | 671 | hbox.show() |
443 | vbox.pack_start(hbox, expand=False, fill=False) | 672 | vbox.pack_start(hbox, expand=False, fill=False) |
444 | cancel = gtk.Button(stock=gtk.STOCK_CANCEL) | 673 | self.back = gtk.Button("Back") |
445 | cancel.connect("clicked", self.cancel_build) | 674 | self.back.show() |
446 | cancel.show() | 675 | self.back.set_sensitive(False) |
447 | hbox.pack_end(cancel, expand=False, fill=False) | 676 | hbox.pack_start(self.back, expand=False, fill=False) |
677 | self.cancel = gtk.Button("Stop Build") | ||
678 | self.cancel.connect("clicked", self.cancel_build) | ||
679 | self.cancel.show() | ||
680 | hbox.pack_end(self.cancel, expand=False, fill=False) | ||
448 | 681 | ||
449 | return vbox | 682 | return vbox |
683 | |||
684 | def create_menu(self): | ||
685 | menu_items = '''<ui> | ||
686 | <menubar name="MenuBar"> | ||
687 | <menu action="File"> | ||
688 | <menuitem action="Save"/> | ||
689 | <menuitem action="Save As"/> | ||
690 | <menuitem action="Open"/> | ||
691 | <separator/> | ||
692 | <menuitem action="AddLayer" label="Add Layer"/> | ||
693 | <separator/> | ||
694 | <menuitem action="Quit"/> | ||
695 | </menu> | ||
696 | <menu action="Edit"> | ||
697 | <menuitem action="Layers" label="Layers"/> | ||
698 | <menuitem action="Preferences"/> | ||
699 | </menu> | ||
700 | <menu action="Help"> | ||
701 | <menuitem action="About"/> | ||
702 | </menu> | ||
703 | </menubar> | ||
704 | </ui>''' | ||
705 | |||
706 | uimanager = gtk.UIManager() | ||
707 | accel = uimanager.get_accel_group() | ||
708 | self.add_accel_group(accel) | ||
709 | |||
710 | actions = gtk.ActionGroup('ImageCreator') | ||
711 | self.actions = actions | ||
712 | actions.add_actions([('Quit', gtk.STOCK_QUIT, None, None, | ||
713 | None, self.menu_quit,), | ||
714 | ('File', None, '_File'), | ||
715 | ('Save', gtk.STOCK_SAVE, None, None, None, self.save_cb), | ||
716 | ('Save As', gtk.STOCK_SAVE_AS, None, None, None, self.save_as_cb), | ||
717 | ('Open', gtk.STOCK_OPEN, None, None, None, self.open_cb), | ||
718 | ('AddLayer', None, 'Add Layer', None, None, self.add_layer_cb), | ||
719 | ('Edit', None, '_Edit'), | ||
720 | ('Help', None, '_Help'), | ||
721 | ('Layers', None, 'Layers', None, None, self.layers_cb), | ||
722 | ('Preferences', gtk.STOCK_PREFERENCES, None, None, None, self.preferences_cb), | ||
723 | ('About', gtk.STOCK_ABOUT, None, None, None, self.about_cb)]) | ||
724 | uimanager.insert_action_group(actions, 0) | ||
725 | uimanager.add_ui_from_string(menu_items) | ||
726 | |||
727 | menubar = uimanager.get_widget('/MenuBar') | ||
728 | menubar.show_all() | ||
729 | |||
730 | return menubar | ||
450 | 731 | ||
451 | def create_build_gui(self): | 732 | def create_build_gui(self): |
452 | vbox = gtk.VBox(False, 6) | 733 | vbox = gtk.VBox(False, 12) |
734 | vbox.set_border_width(6) | ||
453 | vbox.show() | 735 | vbox.show() |
454 | hbox = gtk.HBox(False, 6) | 736 | |
737 | hbox = gtk.HBox(False, 12) | ||
455 | hbox.show() | 738 | hbox.show() |
456 | vbox.pack_start(hbox, expand=False, fill=False) | 739 | vbox.pack_start(hbox, expand=False, fill=False) |
457 | 740 | ||
@@ -459,90 +742,92 @@ class MainWindow (gtk.Window): | |||
459 | label.show() | 742 | label.show() |
460 | hbox.pack_start(label, expand=False, fill=False, padding=6) | 743 | hbox.pack_start(label, expand=False, fill=False, padding=6) |
461 | self.machine_combo = gtk.combo_box_new_text() | 744 | self.machine_combo = gtk.combo_box_new_text() |
462 | self.machine_combo.set_active(0) | ||
463 | self.machine_combo.show() | 745 | self.machine_combo.show() |
464 | self.machine_combo.set_tooltip_text("Selects the architecture of the target board for which you would like to build an image.") | 746 | self.machine_combo.set_tooltip_text("Selects the architecture of the target board for which you would like to build an image.") |
465 | hbox.pack_start(self.machine_combo, expand=False, fill=False, padding=6) | 747 | hbox.pack_start(self.machine_combo, expand=False, fill=False, padding=6) |
748 | label = gtk.Label("Base image:") | ||
749 | label.show() | ||
750 | hbox.pack_start(label, expand=False, fill=False, padding=6) | ||
751 | self.image_combo = gtk.ComboBox() | ||
752 | self.image_combo.show() | ||
753 | self.image_combo.set_tooltip_text("Selects the image on which to base the created image") | ||
754 | image_combo_cell = gtk.CellRendererText() | ||
755 | self.image_combo.pack_start(image_combo_cell, True) | ||
756 | self.image_combo.add_attribute(image_combo_cell, 'text', self.model.COL_NAME) | ||
757 | hbox.pack_start(self.image_combo, expand=False, fill=False, padding=6) | ||
758 | self.progress = gtk.ProgressBar() | ||
759 | self.progress.set_size_request(250, -1) | ||
760 | hbox.pack_end(self.progress, expand=False, fill=False, padding=6) | ||
466 | 761 | ||
467 | ins = gtk.Notebook() | 762 | ins = gtk.Notebook() |
468 | vbox.pack_start(ins, expand=True, fill=True) | 763 | vbox.pack_start(ins, expand=True, fill=True) |
469 | ins.set_show_tabs(True) | 764 | ins.set_show_tabs(True) |
470 | label = gtk.Label("Images") | 765 | label = gtk.Label("Packages") |
471 | label.show() | 766 | label.show() |
472 | ins.append_page(self.images(), tab_label=label) | 767 | ins.append_page(self.pkgsaz(), tab_label=label) |
473 | label = gtk.Label("Tasks") | 768 | label = gtk.Label("Package Collections") |
474 | label.show() | 769 | label.show() |
475 | ins.append_page(self.tasks(), tab_label=label) | 770 | ins.append_page(self.tasks(), tab_label=label) |
476 | label = gtk.Label("Packages (by Group)") | ||
477 | label.show() | ||
478 | ins.append_page(self.pkgsgrp(), tab_label=label) | ||
479 | label = gtk.Label("Packages (by Name)") | ||
480 | label.show() | ||
481 | ins.append_page(self.pkgsaz(), tab_label=label) | ||
482 | ins.set_current_page(0) | 771 | ins.set_current_page(0) |
483 | ins.show_all() | 772 | ins.show_all() |
484 | 773 | ||
485 | hbox = gtk.HBox() | ||
486 | hbox.show() | ||
487 | vbox.pack_start(hbox, expand=False, fill=False) | ||
488 | label = gtk.Label("Image contents:") | 774 | label = gtk.Label("Image contents:") |
775 | self.model.connect("contents-changed", self.update_package_count_cb, label) | ||
776 | label.set_property("xalign", 0.00) | ||
489 | label.show() | 777 | label.show() |
490 | hbox.pack_start(label, expand=False, fill=False, padding=6) | 778 | vbox.pack_start(label, expand=False, fill=False, padding=6) |
491 | con = self.contents() | 779 | con = self.contents() |
492 | con.show() | 780 | con.show() |
493 | vbox.pack_start(con, expand=True, fill=True) | 781 | vbox.pack_start(con, expand=True, fill=True) |
494 | 782 | ||
495 | #advanced = gtk.Expander(label="Advanced") | 783 | bbox = gtk.HButtonBox() |
496 | #advanced.connect("notify::expanded", self.advanced_expander_cb) | 784 | bbox.set_spacing(12) |
497 | #advanced.show() | 785 | bbox.set_layout(gtk.BUTTONBOX_END) |
498 | #vbox.pack_start(advanced, expand=False, fill=False) | 786 | bbox.show() |
499 | 787 | vbox.pack_start(bbox, expand=False, fill=False) | |
500 | hbox = gtk.HBox() | ||
501 | hbox.show() | ||
502 | vbox.pack_start(hbox, expand=False, fill=False) | ||
503 | bake = gtk.Button("Bake") | ||
504 | bake.connect("clicked", self.bake_clicked_cb) | ||
505 | bake.show() | ||
506 | hbox.pack_end(bake, expand=False, fill=False, padding=6) | ||
507 | reset = gtk.Button("Reset") | 788 | reset = gtk.Button("Reset") |
508 | reset.connect("clicked", self.reset_clicked_cb) | 789 | reset.connect("clicked", self.reset_clicked_cb) |
509 | reset.show() | 790 | reset.show() |
510 | hbox.pack_end(reset, expand=False, fill=False, padding=6) | 791 | bbox.add(reset) |
792 | bake = gtk.Button("Bake") | ||
793 | bake.connect("clicked", self.bake_clicked_cb) | ||
794 | bake.show() | ||
795 | bbox.add(bake) | ||
511 | 796 | ||
512 | return vbox | 797 | return vbox |
513 | 798 | ||
799 | def update_package_count_cb(self, model, count, label): | ||
800 | lbl = "Image contents (%s packages):" % count | ||
801 | label.set_text(lbl) | ||
802 | |||
514 | def contents(self): | 803 | def contents(self): |
515 | self.contents_tree = gtk.TreeView() | 804 | self.contents_tree = gtk.TreeView() |
516 | self.contents_tree.set_headers_visible(True) | 805 | self.contents_tree.set_headers_visible(True) |
517 | self.contents_tree.get_selection().set_mode(gtk.SELECTION_NONE) | 806 | self.contents_tree.get_selection().set_mode(gtk.SELECTION_SINGLE) |
518 | 807 | ||
519 | # allow searching in the package column | 808 | # allow searching in the package column |
520 | self.contents_tree.set_search_column(0) | 809 | self.contents_tree.set_search_column(0) |
810 | self.contents_tree.set_enable_search(True) | ||
521 | 811 | ||
522 | col = gtk.TreeViewColumn('Package') | 812 | col = gtk.TreeViewColumn('Package') |
523 | col.set_sort_column_id(0) | 813 | col.set_sort_column_id(0) |
814 | col.set_min_width(430) | ||
524 | col1 = gtk.TreeViewColumn('Brought in by') | 815 | col1 = gtk.TreeViewColumn('Brought in by') |
525 | col1.set_resizable(True) | 816 | col1.set_resizable(True) |
526 | col2 = gtk.TreeViewColumn('Remove') | 817 | col1.set_min_width(430) |
527 | col2.set_expand(False) | ||
528 | 818 | ||
529 | self.contents_tree.append_column(col) | 819 | self.contents_tree.append_column(col) |
530 | self.contents_tree.append_column(col1) | 820 | self.contents_tree.append_column(col1) |
531 | self.contents_tree.append_column(col2) | ||
532 | 821 | ||
533 | cell = gtk.CellRendererText() | 822 | cell = gtk.CellRendererText() |
534 | cell1 = gtk.CellRendererText() | 823 | cell1 = gtk.CellRendererText() |
535 | cell1.set_property('width-chars', 20) | 824 | cell1.set_property('width-chars', 20) |
536 | cell2 = gtk.CellRendererToggle() | ||
537 | cell2.connect("toggled", self.remove_package_cb) | ||
538 | 825 | ||
539 | col.pack_start(cell, True) | 826 | col.pack_start(cell, True) |
540 | col1.pack_start(cell1, True) | 827 | col1.pack_start(cell1, True) |
541 | col2.pack_start(cell2, True) | ||
542 | 828 | ||
543 | col.set_attributes(cell, text=self.model.COL_NAME) | 829 | col.set_attributes(cell, text=self.model.COL_NAME) |
544 | col1.set_attributes(cell1, text=self.model.COL_BINB) | 830 | col1.set_attributes(cell1, text=self.model.COL_BINB) |
545 | col2.set_attributes(cell2, active=self.model.COL_INC) | ||
546 | 831 | ||
547 | self.contents_tree.show() | 832 | self.contents_tree.show() |
548 | 833 | ||
@@ -554,26 +839,67 @@ class MainWindow (gtk.Window): | |||
554 | return scroll | 839 | return scroll |
555 | 840 | ||
556 | def main (server, eventHandler): | 841 | def main (server, eventHandler): |
842 | import multiprocessing | ||
843 | cpu_cnt = multiprocessing.cpu_count() | ||
844 | |||
557 | gobject.threads_init() | 845 | gobject.threads_init() |
558 | 846 | ||
559 | taskmodel = TaskListModel() | 847 | taskmodel = TaskListModel() |
848 | configurator = Configurator() | ||
560 | handler = HobHandler(taskmodel, server) | 849 | handler = HobHandler(taskmodel, server) |
561 | mach = server.runCommand(["getVariable", "MACHINE"]) | 850 | mach = server.runCommand(["getVariable", "MACHINE"]) |
851 | sdk_mach = server.runCommand(["getVariable", "SDKMACHINE"]) | ||
852 | # If SDKMACHINE not set the default SDK_ARCH is used so we | ||
853 | # should represent that in the GUI | ||
854 | if not sdk_mach: | ||
855 | sdk_mach = server.runCommand(["getVariable", "SDK_ARCH"]) | ||
562 | distro = server.runCommand(["getVariable", "DISTRO"]) | 856 | distro = server.runCommand(["getVariable", "DISTRO"]) |
563 | 857 | bbthread = server.runCommand(["getVariable", "BB_NUMBER_THREADS"]) | |
564 | window = MainWindow(taskmodel, handler, mach, distro) | 858 | if not bbthread: |
859 | bbthread = cpu_cnt | ||
860 | handler.set_bbthreads(cpu_cnt) | ||
861 | else: | ||
862 | bbthread = int(bbthread) | ||
863 | pmake = server.runCommand(["getVariable", "PARALLEL_MAKE"]) | ||
864 | if not pmake: | ||
865 | pmake = cpu_cnt | ||
866 | handler.set_pmake(cpu_cnt) | ||
867 | else: | ||
868 | # The PARALLEL_MAKE variable will be of the format: "-j 3" and we only | ||
869 | # want a number for the spinner, so strip everything from the variable | ||
870 | # up to and including the space | ||
871 | pmake = int(pmake[pmake.find(" ")+1:]) | ||
872 | |||
873 | image_types = server.runCommand(["getVariable", "IMAGE_TYPES"]) | ||
874 | |||
875 | pclasses = server.runCommand(["getVariable", "PACKAGE_CLASSES"]).split(" ") | ||
876 | # NOTE: we're only supporting one value for PACKAGE_CLASSES being set | ||
877 | # this seems OK because we're using the first package format set in | ||
878 | # PACKAGE_CLASSES and that's the package manager used for the rootfs | ||
879 | pkg, sep, pclass = pclasses[0].rpartition("_") | ||
880 | |||
881 | prefs = HobPrefs(configurator, handler, sdk_mach, distro, pclass, cpu_cnt, | ||
882 | pmake, bbthread, image_types) | ||
883 | layers = LayerEditor(configurator, None) | ||
884 | window = MainWindow(taskmodel, handler, configurator, prefs, layers, mach) | ||
885 | prefs.set_parent_window(window) | ||
886 | layers.set_parent_window(window) | ||
565 | window.show_all () | 887 | window.show_all () |
566 | handler.connect("machines-updated", window.update_machines) | 888 | handler.connect("machines-updated", window.update_machines) |
567 | handler.connect("distros-updated", window.update_distros) | 889 | handler.connect("sdk-machines-updated", prefs.update_sdk_machines) |
890 | handler.connect("distros-updated", prefs.update_distros) | ||
891 | handler.connect("package-formats-found", prefs.update_package_formats) | ||
568 | handler.connect("generating-data", window.busy) | 892 | handler.connect("generating-data", window.busy) |
569 | handler.connect("data-generated", window.data_generated) | 893 | handler.connect("data-generated", window.data_generated) |
570 | pbar = ProgressBar(window) | 894 | handler.connect("reload-triggered", window.reload_triggered_cb) |
571 | pbar.connect("delete-event", gtk.main_quit) | 895 | configurator.connect("layers-loaded", layers.load_current_layers) |
896 | configurator.connect("layers-changed", handler.reload_data) | ||
897 | handler.connect("config-found", configurator.configFound) | ||
572 | 898 | ||
573 | try: | 899 | try: |
574 | # kick the while thing off | 900 | # kick the while thing off |
575 | handler.current_command = "findConfigFilesDistro" | 901 | handler.current_command = "findConfigFilePathLocal" |
576 | server.runCommand(["findConfigFiles", "DISTRO"]) | 902 | server.runCommand(["findConfigFilePath", "local.conf"]) |
577 | except xmlrpclib.Fault: | 903 | except xmlrpclib.Fault: |
578 | print("XMLRPC Fault getting commandline:\n %s" % x) | 904 | print("XMLRPC Fault getting commandline:\n %s" % x) |
579 | return 1 | 905 | return 1 |
@@ -584,7 +910,7 @@ def main (server, eventHandler): | |||
584 | handler.event_handle_idle_func, | 910 | handler.event_handle_idle_func, |
585 | eventHandler, | 911 | eventHandler, |
586 | window.build, | 912 | window.build, |
587 | pbar) | 913 | window.progress) |
588 | 914 | ||
589 | try: | 915 | try: |
590 | gtk.main() | 916 | gtk.main() |