diff options
Diffstat (limited to 'bitbake/lib/bb/ui/crumbs/packageselectionpage.py')
-rwxr-xr-x | bitbake/lib/bb/ui/crumbs/packageselectionpage.py | 355 |
1 files changed, 355 insertions, 0 deletions
diff --git a/bitbake/lib/bb/ui/crumbs/packageselectionpage.py b/bitbake/lib/bb/ui/crumbs/packageselectionpage.py new file mode 100755 index 0000000000..7c62b36e6b --- /dev/null +++ b/bitbake/lib/bb/ui/crumbs/packageselectionpage.py | |||
@@ -0,0 +1,355 @@ | |||
1 | #!/usr/bin/env python | ||
2 | # | ||
3 | # BitBake Graphical GTK User Interface | ||
4 | # | ||
5 | # Copyright (C) 2012 Intel Corporation | ||
6 | # | ||
7 | # Authored by Dongxiao Xu <dongxiao.xu@intel.com> | ||
8 | # Authored by Shane Wang <shane.wang@intel.com> | ||
9 | # | ||
10 | # This program is free software; you can redistribute it and/or modify | ||
11 | # it under the terms of the GNU General Public License version 2 as | ||
12 | # published by the Free Software Foundation. | ||
13 | # | ||
14 | # This program is distributed in the hope that it will be useful, | ||
15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | # GNU General Public License for more details. | ||
18 | # | ||
19 | # You should have received a copy of the GNU General Public License along | ||
20 | # with this program; if not, write to the Free Software Foundation, Inc., | ||
21 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
22 | |||
23 | import gtk | ||
24 | import glib | ||
25 | from bb.ui.crumbs.hobcolor import HobColors | ||
26 | from bb.ui.crumbs.hobwidget import HobViewTable, HobNotebook, HobAltButton, HobButton | ||
27 | from bb.ui.crumbs.hoblistmodel import PackageListModel | ||
28 | from bb.ui.crumbs.hobpages import HobPage | ||
29 | |||
30 | # | ||
31 | # PackageSelectionPage | ||
32 | # | ||
33 | class PackageSelectionPage (HobPage): | ||
34 | |||
35 | pages = [ | ||
36 | { | ||
37 | 'name' : 'Included packages', | ||
38 | 'tooltip' : 'The packages currently included for your image', | ||
39 | 'filter' : { PackageListModel.COL_INC : [True] }, | ||
40 | 'search' : 'Search packages by name', | ||
41 | 'searchtip' : 'Enter a package name to find it', | ||
42 | 'columns' : [{ | ||
43 | 'col_name' : 'Package name', | ||
44 | 'col_id' : PackageListModel.COL_NAME, | ||
45 | 'col_style': 'text', | ||
46 | 'col_min' : 100, | ||
47 | 'col_max' : 300, | ||
48 | 'expand' : 'True' | ||
49 | }, { | ||
50 | 'col_name' : 'Size', | ||
51 | 'col_id' : PackageListModel.COL_SIZE, | ||
52 | 'col_style': 'text', | ||
53 | 'col_min' : 100, | ||
54 | 'col_max' : 300, | ||
55 | 'expand' : 'True' | ||
56 | }, { | ||
57 | 'col_name' : 'Recipe', | ||
58 | 'col_id' : PackageListModel.COL_RCP, | ||
59 | 'col_style': 'text', | ||
60 | 'col_min' : 100, | ||
61 | 'col_max' : 250, | ||
62 | 'expand' : 'True' | ||
63 | }, { | ||
64 | 'col_name' : 'Brought in by (+others)', | ||
65 | 'col_id' : PackageListModel.COL_BINB, | ||
66 | 'col_style': 'binb', | ||
67 | 'col_min' : 100, | ||
68 | 'col_max' : 350, | ||
69 | 'expand' : 'True' | ||
70 | }, { | ||
71 | 'col_name' : 'Included', | ||
72 | 'col_id' : PackageListModel.COL_INC, | ||
73 | 'col_style': 'check toggle', | ||
74 | 'col_min' : 100, | ||
75 | 'col_max' : 100 | ||
76 | }] | ||
77 | }, { | ||
78 | 'name' : 'All packages', | ||
79 | 'tooltip' : 'All packages that have been built', | ||
80 | 'filter' : {}, | ||
81 | 'search' : 'Search packages by name', | ||
82 | 'searchtip' : 'Enter a package name to find it', | ||
83 | 'columns' : [{ | ||
84 | 'col_name' : 'Package name', | ||
85 | 'col_id' : PackageListModel.COL_NAME, | ||
86 | 'col_style': 'text', | ||
87 | 'col_min' : 100, | ||
88 | 'col_max' : 400, | ||
89 | 'expand' : 'True' | ||
90 | }, { | ||
91 | 'col_name' : 'Size', | ||
92 | 'col_id' : PackageListModel.COL_SIZE, | ||
93 | 'col_style': 'text', | ||
94 | 'col_min' : 100, | ||
95 | 'col_max' : 500, | ||
96 | 'expand' : 'True' | ||
97 | }, { | ||
98 | 'col_name' : 'Recipe', | ||
99 | 'col_id' : PackageListModel.COL_RCP, | ||
100 | 'col_style': 'text', | ||
101 | 'col_min' : 100, | ||
102 | 'col_max' : 250, | ||
103 | 'expand' : 'True' | ||
104 | }, { | ||
105 | 'col_name' : 'Included', | ||
106 | 'col_id' : PackageListModel.COL_INC, | ||
107 | 'col_style': 'check toggle', | ||
108 | 'col_min' : 100, | ||
109 | 'col_max' : 100 | ||
110 | }] | ||
111 | } | ||
112 | ] | ||
113 | |||
114 | (INCLUDED, | ||
115 | ALL) = range(2) | ||
116 | |||
117 | def __init__(self, builder): | ||
118 | super(PackageSelectionPage, self).__init__(builder, "Edit packages") | ||
119 | |||
120 | # set invisible members | ||
121 | self.recipe_model = self.builder.recipe_model | ||
122 | self.package_model = self.builder.package_model | ||
123 | |||
124 | # create visual elements | ||
125 | self.create_visual_elements() | ||
126 | |||
127 | def included_clicked_cb(self, button): | ||
128 | self.ins.set_current_page(self.INCLUDED) | ||
129 | |||
130 | def create_visual_elements(self): | ||
131 | self.label = gtk.Label("Packages included: 0\nSelected packages size: 0 MB") | ||
132 | self.eventbox = self.add_onto_top_bar(self.label, 73) | ||
133 | self.pack_start(self.eventbox, expand=False, fill=False) | ||
134 | self.pack_start(self.group_align, expand=True, fill=True) | ||
135 | |||
136 | # set visible members | ||
137 | self.ins = HobNotebook() | ||
138 | self.tables = [] # we need to modify table when the dialog is shown | ||
139 | |||
140 | search_names = [] | ||
141 | search_tips = [] | ||
142 | # append the tab | ||
143 | for page in self.pages: | ||
144 | columns = page['columns'] | ||
145 | name = page['name'] | ||
146 | tab = HobViewTable(columns, name) | ||
147 | search_names.append(page['search']) | ||
148 | search_tips.append(page['searchtip']) | ||
149 | filter = page['filter'] | ||
150 | sort_model = self.package_model.tree_model(filter, initial=True) | ||
151 | tab.set_model(sort_model) | ||
152 | tab.connect("toggled", self.table_toggled_cb, name) | ||
153 | tab.connect("button-release-event", self.button_click_cb) | ||
154 | tab.connect("cell-fadeinout-stopped", self.after_fadeout_checkin_include, filter) | ||
155 | self.ins.append_page(tab, page['name'], page['tooltip']) | ||
156 | self.tables.append(tab) | ||
157 | |||
158 | self.ins.set_entry(search_names, search_tips) | ||
159 | self.ins.search.connect("changed", self.search_entry_changed) | ||
160 | |||
161 | # add all into the dialog | ||
162 | self.box_group_area.pack_start(self.ins, expand=True, fill=True) | ||
163 | |||
164 | self.button_box = gtk.HBox(False, 6) | ||
165 | self.box_group_area.pack_start(self.button_box, expand=False, fill=False) | ||
166 | |||
167 | self.build_image_button = HobButton('Build image') | ||
168 | #self.build_image_button.set_size_request(205, 49) | ||
169 | self.build_image_button.set_tooltip_text("Build target image") | ||
170 | self.build_image_button.set_flags(gtk.CAN_DEFAULT) | ||
171 | self.build_image_button.grab_default() | ||
172 | self.build_image_button.connect("clicked", self.build_image_clicked_cb) | ||
173 | self.button_box.pack_end(self.build_image_button, expand=False, fill=False) | ||
174 | |||
175 | self.back_button = HobAltButton('Cancel') | ||
176 | self.back_button.connect("clicked", self.back_button_clicked_cb) | ||
177 | self.button_box.pack_end(self.back_button, expand=False, fill=False) | ||
178 | |||
179 | def search_entry_changed(self, entry): | ||
180 | text = entry.get_text() | ||
181 | if self.ins.search_focus: | ||
182 | self.ins.search_focus = False | ||
183 | elif self.ins.page_changed: | ||
184 | self.ins.page_change = False | ||
185 | self.filter_search(entry) | ||
186 | elif text not in self.ins.search_names: | ||
187 | self.filter_search(entry) | ||
188 | |||
189 | def filter_search(self, entry): | ||
190 | text = entry.get_text() | ||
191 | current_tab = self.ins.get_current_page() | ||
192 | filter = self.pages[current_tab]['filter'] | ||
193 | filter[PackageListModel.COL_NAME] = text | ||
194 | self.tables[current_tab].set_model(self.package_model.tree_model(filter, search_data=text)) | ||
195 | if self.package_model.filtered_nb == 0: | ||
196 | if not self.ins.get_nth_page(current_tab).top_bar: | ||
197 | self.ins.get_nth_page(current_tab).add_no_result_bar(entry) | ||
198 | self.ins.get_nth_page(current_tab).top_bar.set_no_show_all(True) | ||
199 | self.ins.get_nth_page(current_tab).top_bar.show() | ||
200 | self.ins.get_nth_page(current_tab).scroll.hide() | ||
201 | else: | ||
202 | if self.ins.get_nth_page(current_tab).top_bar: | ||
203 | self.ins.get_nth_page(current_tab).top_bar.hide() | ||
204 | self.ins.get_nth_page(current_tab).scroll.show() | ||
205 | if entry.get_text() == '': | ||
206 | entry.set_icon_sensitive(gtk.ENTRY_ICON_SECONDARY, False) | ||
207 | else: | ||
208 | entry.set_icon_sensitive(gtk.ENTRY_ICON_SECONDARY, True) | ||
209 | |||
210 | def button_click_cb(self, widget, event): | ||
211 | path, col = widget.table_tree.get_cursor() | ||
212 | tree_model = widget.table_tree.get_model() | ||
213 | if path and col.get_title() != 'Included': # else activation is likely a removal | ||
214 | properties = {'binb': '' , 'name': '', 'size':'', 'recipe':'', 'files_list':''} | ||
215 | properties['binb'] = tree_model.get_value(tree_model.get_iter(path), PackageListModel.COL_BINB) | ||
216 | properties['name'] = tree_model.get_value(tree_model.get_iter(path), PackageListModel.COL_NAME) | ||
217 | properties['size'] = tree_model.get_value(tree_model.get_iter(path), PackageListModel.COL_SIZE) | ||
218 | properties['recipe'] = tree_model.get_value(tree_model.get_iter(path), PackageListModel.COL_RCP) | ||
219 | properties['files_list'] = tree_model.get_value(tree_model.get_iter(path), PackageListModel.COL_FLIST) | ||
220 | |||
221 | self.builder.show_recipe_property_dialog(properties) | ||
222 | |||
223 | def open_log_clicked_cb(self, button, log_file): | ||
224 | if log_file: | ||
225 | log_file = "file:///" + log_file | ||
226 | gtk.show_uri(screen=button.get_screen(), uri=log_file, timestamp=0) | ||
227 | |||
228 | def show_page(self, log_file): | ||
229 | children = self.button_box.get_children() or [] | ||
230 | for child in children: | ||
231 | self.button_box.remove(child) | ||
232 | # re-packed the buttons as request, add the 'open log' button if build success | ||
233 | self.button_box.pack_end(self.build_image_button, expand=False, fill=False) | ||
234 | if log_file: | ||
235 | open_log_button = HobAltButton("Open log") | ||
236 | open_log_button.connect("clicked", self.open_log_clicked_cb, log_file) | ||
237 | open_log_button.set_tooltip_text("Open the build's log file") | ||
238 | self.button_box.pack_end(open_log_button, expand=False, fill=False) | ||
239 | self.button_box.pack_end(self.back_button, expand=False, fill=False) | ||
240 | self.show_all() | ||
241 | |||
242 | def build_image_clicked_cb(self, button): | ||
243 | self.builder.parsing_warnings = [] | ||
244 | self.builder.build_image() | ||
245 | |||
246 | def refresh_tables(self): | ||
247 | self.ins.reset_entry(self.ins.search, 0) | ||
248 | for tab in self.tables: | ||
249 | index = self.tables.index(tab) | ||
250 | filter = self.pages[index]['filter'] | ||
251 | tab.set_model(self.package_model.tree_model(filter, initial=True)) | ||
252 | |||
253 | def back_button_clicked_cb(self, button): | ||
254 | if self.builder.previous_step == self.builder.IMAGE_GENERATED: | ||
255 | self.builder.restore_initial_selected_packages() | ||
256 | self.refresh_selection() | ||
257 | self.builder.show_image_details() | ||
258 | else: | ||
259 | self.builder.show_configuration() | ||
260 | self.refresh_tables() | ||
261 | |||
262 | def refresh_selection(self): | ||
263 | self.builder.configuration.selected_packages = self.package_model.get_selected_packages() | ||
264 | self.builder.configuration.user_selected_packages = self.package_model.get_user_selected_packages() | ||
265 | selected_packages_num = len(self.builder.configuration.selected_packages) | ||
266 | selected_packages_size = self.package_model.get_packages_size() | ||
267 | selected_packages_size_str = HobPage._size_to_string(selected_packages_size) | ||
268 | |||
269 | if self.builder.configuration.image_packages == self.builder.configuration.selected_packages: | ||
270 | image_total_size_str = self.builder.configuration.image_size | ||
271 | else: | ||
272 | image_overhead_factor = self.builder.configuration.image_overhead_factor | ||
273 | image_rootfs_size = self.builder.configuration.image_rootfs_size / 1024 # image_rootfs_size is KB | ||
274 | image_extra_size = self.builder.configuration.image_extra_size / 1024 # image_extra_size is KB | ||
275 | base_size = image_overhead_factor * selected_packages_size | ||
276 | image_total_size = max(base_size, image_rootfs_size) + image_extra_size | ||
277 | if "zypper" in self.builder.configuration.selected_packages: | ||
278 | image_total_size += (51200 * 1024) | ||
279 | image_total_size_str = HobPage._size_to_string(image_total_size) | ||
280 | |||
281 | self.label.set_label("Packages included: %s\nSelected packages size: %s\nEstimated image size: %s" % | ||
282 | (selected_packages_num, selected_packages_size_str, image_total_size_str)) | ||
283 | self.ins.show_indicator_icon("Included packages", selected_packages_num) | ||
284 | |||
285 | def toggle_item_idle_cb(self, path, view_tree, cell, pagename): | ||
286 | if not self.package_model.path_included(path): | ||
287 | self.package_model.include_item(item_path=path, binb="User Selected") | ||
288 | else: | ||
289 | self.pre_fadeout_checkout_include(view_tree) | ||
290 | self.package_model.exclude_item(item_path=path) | ||
291 | self.render_fadeout(view_tree, cell) | ||
292 | |||
293 | self.refresh_selection() | ||
294 | if not self.builder.customized: | ||
295 | self.builder.customized = True | ||
296 | self.builder.set_base_image() | ||
297 | self.builder.configuration.selected_image = self.recipe_model.__custom_image__ | ||
298 | self.builder.rcppkglist_populated() | ||
299 | |||
300 | self.builder.window_sensitive(True) | ||
301 | view_model = view_tree.get_model() | ||
302 | vpath = self.package_model.convert_path_to_vpath(view_model, path) | ||
303 | view_tree.set_cursor(vpath) | ||
304 | |||
305 | def table_toggled_cb(self, table, cell, view_path, toggled_columnid, view_tree, pagename): | ||
306 | # Click to include a package | ||
307 | self.builder.window_sensitive(False) | ||
308 | view_model = view_tree.get_model() | ||
309 | path = self.package_model.convert_vpath_to_path(view_model, view_path) | ||
310 | glib.idle_add(self.toggle_item_idle_cb, path, view_tree, cell, pagename) | ||
311 | |||
312 | def pre_fadeout_checkout_include(self, tree): | ||
313 | #after the fadeout the table will be sorted as before | ||
314 | self.sort_column_id = self.package_model.sort_column_id | ||
315 | self.sort_order = self.package_model.sort_order | ||
316 | |||
317 | self.package_model.resync_fadeout_column(self.package_model.get_iter_first()) | ||
318 | # Check out a model which base on the column COL_FADE_INC, | ||
319 | # it's save the prev state of column COL_INC before do exclude_item | ||
320 | filter = { PackageListModel.COL_FADE_INC : [True]} | ||
321 | new_model = self.package_model.tree_model(filter, excluded_items_ahead=True) | ||
322 | tree.set_model(new_model) | ||
323 | tree.expand_all() | ||
324 | |||
325 | def get_excluded_rows(self, to_render_cells, model, it): | ||
326 | while it: | ||
327 | path = model.get_path(it) | ||
328 | prev_cell_is_active = model.get_value(it, PackageListModel.COL_FADE_INC) | ||
329 | curr_cell_is_active = model.get_value(it, PackageListModel.COL_INC) | ||
330 | if (prev_cell_is_active == True) and (curr_cell_is_active == False): | ||
331 | to_render_cells.append(path) | ||
332 | if model.iter_has_child(it): | ||
333 | self.get_excluded_rows(to_render_cells, model, model.iter_children(it)) | ||
334 | it = model.iter_next(it) | ||
335 | |||
336 | return to_render_cells | ||
337 | |||
338 | def render_fadeout(self, tree, cell): | ||
339 | if (not cell) or (not tree): | ||
340 | return | ||
341 | to_render_cells = [] | ||
342 | view_model = tree.get_model() | ||
343 | self.get_excluded_rows(to_render_cells, view_model, view_model.get_iter_first()) | ||
344 | |||
345 | cell.fadeout(tree, 1000, to_render_cells) | ||
346 | |||
347 | def after_fadeout_checkin_include(self, table, ctrl, cell, tree, filter): | ||
348 | self.package_model.sort_column_id = self.sort_column_id | ||
349 | self.package_model.sort_order = self.sort_order | ||
350 | tree.set_model(self.package_model.tree_model(filter)) | ||
351 | tree.expand_all() | ||
352 | |||
353 | def set_packages_curr_tab(self, curr_page): | ||
354 | self.ins.set_current_page(curr_page) | ||
355 | |||