diff options
-rw-r--r-- | scripts/pkgdataui.glade | 335 | ||||
-rwxr-xr-x | scripts/pkgdataui.py | 241 |
2 files changed, 576 insertions, 0 deletions
diff --git a/scripts/pkgdataui.glade b/scripts/pkgdataui.glade new file mode 100644 index 0000000000..04e987b975 --- /dev/null +++ b/scripts/pkgdataui.glade | |||
@@ -0,0 +1,335 @@ | |||
1 | <?xml version="1.0" encoding="UTF-8"?> | ||
2 | <!-- Generated with glade 3.18.3 --> | ||
3 | <interface> | ||
4 | <requires lib="gtk+" version="3.12"/> | ||
5 | <object class="GtkListStore" id="file_store"> | ||
6 | <columns> | ||
7 | <!-- column-name Filename --> | ||
8 | <column type="gchararray"/> | ||
9 | <!-- column-name Size --> | ||
10 | <column type="glong"/> | ||
11 | </columns> | ||
12 | </object> | ||
13 | <object class="GtkListStore" id="package_store"> | ||
14 | <columns> | ||
15 | <!-- column-name Package --> | ||
16 | <column type="gchararray"/> | ||
17 | <!-- column-name Size --> | ||
18 | <column type="glong"/> | ||
19 | </columns> | ||
20 | </object> | ||
21 | <object class="GtkListStore" id="pkgdata_store"> | ||
22 | <columns> | ||
23 | <!-- column-name Name --> | ||
24 | <column type="gchararray"/> | ||
25 | <!-- column-name Path --> | ||
26 | <column type="gchararray"/> | ||
27 | </columns> | ||
28 | </object> | ||
29 | <object class="GtkListStore" id="recipe_store"> | ||
30 | <columns> | ||
31 | <!-- column-name Recipe --> | ||
32 | <column type="gchararray"/> | ||
33 | </columns> | ||
34 | </object> | ||
35 | <object class="GtkWindow" id="window"> | ||
36 | <property name="can_focus">False</property> | ||
37 | <property name="title" translatable="yes">Package Data Browser</property> | ||
38 | <property name="icon_name">accessories-dictionary</property> | ||
39 | <property name="has_resize_grip">True</property> | ||
40 | <child> | ||
41 | <object class="GtkBox" id="box1"> | ||
42 | <property name="visible">True</property> | ||
43 | <property name="can_focus">False</property> | ||
44 | <property name="margin_left">4</property> | ||
45 | <property name="margin_right">4</property> | ||
46 | <property name="margin_top">4</property> | ||
47 | <property name="margin_bottom">4</property> | ||
48 | <property name="orientation">vertical</property> | ||
49 | <property name="spacing">4</property> | ||
50 | <child> | ||
51 | <object class="GtkComboBox" id="pkgdata_combo"> | ||
52 | <property name="can_focus">False</property> | ||
53 | <property name="model">pkgdata_store</property> | ||
54 | <property name="id_column">1</property> | ||
55 | <child> | ||
56 | <object class="GtkCellRendererText" id="cellrenderertext5"/> | ||
57 | <attributes> | ||
58 | <attribute name="text">0</attribute> | ||
59 | </attributes> | ||
60 | </child> | ||
61 | </object> | ||
62 | <packing> | ||
63 | <property name="expand">False</property> | ||
64 | <property name="fill">True</property> | ||
65 | <property name="position">0</property> | ||
66 | </packing> | ||
67 | </child> | ||
68 | <child> | ||
69 | <object class="GtkPaned" id="paned1"> | ||
70 | <property name="visible">True</property> | ||
71 | <property name="can_focus">True</property> | ||
72 | <property name="position">200</property> | ||
73 | <property name="position_set">True</property> | ||
74 | <child> | ||
75 | <object class="GtkScrolledWindow" id="scrolledwindow1"> | ||
76 | <property name="visible">True</property> | ||
77 | <property name="can_focus">True</property> | ||
78 | <property name="shadow_type">in</property> | ||
79 | <property name="min_content_width">100</property> | ||
80 | <child> | ||
81 | <object class="GtkTreeView" id="recipe_view"> | ||
82 | <property name="visible">True</property> | ||
83 | <property name="can_focus">True</property> | ||
84 | <property name="model">recipe_store</property> | ||
85 | <property name="search_column">0</property> | ||
86 | <property name="fixed_height_mode">True</property> | ||
87 | <property name="show_expanders">False</property> | ||
88 | <child internal-child="selection"> | ||
89 | <object class="GtkTreeSelection" id="treeview-selection1"/> | ||
90 | </child> | ||
91 | <child> | ||
92 | <object class="GtkTreeViewColumn" id="treeviewcolumn1"> | ||
93 | <property name="sizing">fixed</property> | ||
94 | <property name="title" translatable="yes">Recipe</property> | ||
95 | <child> | ||
96 | <object class="GtkCellRendererText" id="cellrenderertext1"/> | ||
97 | <attributes> | ||
98 | <attribute name="text">0</attribute> | ||
99 | </attributes> | ||
100 | </child> | ||
101 | </object> | ||
102 | </child> | ||
103 | </object> | ||
104 | </child> | ||
105 | </object> | ||
106 | <packing> | ||
107 | <property name="resize">False</property> | ||
108 | <property name="shrink">True</property> | ||
109 | </packing> | ||
110 | </child> | ||
111 | <child> | ||
112 | <object class="GtkPaned" id="paned2"> | ||
113 | <property name="visible">True</property> | ||
114 | <property name="can_focus">True</property> | ||
115 | <property name="position">200</property> | ||
116 | <property name="position_set">True</property> | ||
117 | <child> | ||
118 | <object class="GtkScrolledWindow" id="scrolledwindow2"> | ||
119 | <property name="visible">True</property> | ||
120 | <property name="can_focus">True</property> | ||
121 | <property name="shadow_type">in</property> | ||
122 | <property name="min_content_width">100</property> | ||
123 | <child> | ||
124 | <object class="GtkTreeView" id="package_view"> | ||
125 | <property name="visible">True</property> | ||
126 | <property name="can_focus">True</property> | ||
127 | <property name="model">package_store</property> | ||
128 | <property name="search_column">0</property> | ||
129 | <property name="show_expanders">False</property> | ||
130 | <child internal-child="selection"> | ||
131 | <object class="GtkTreeSelection" id="treeview-selection2"/> | ||
132 | </child> | ||
133 | <child> | ||
134 | <object class="GtkTreeViewColumn" id="package_name_column"> | ||
135 | <property name="resizable">True</property> | ||
136 | <property name="sizing">autosize</property> | ||
137 | <property name="title" translatable="yes">Package</property> | ||
138 | <property name="sort_column_id">0</property> | ||
139 | <child> | ||
140 | <object class="GtkCellRendererText" id="cellrenderertext2"/> | ||
141 | <attributes> | ||
142 | <attribute name="text">0</attribute> | ||
143 | </attributes> | ||
144 | </child> | ||
145 | </object> | ||
146 | </child> | ||
147 | <child> | ||
148 | <object class="GtkTreeViewColumn" id="package_size_column"> | ||
149 | <property name="resizable">True</property> | ||
150 | <property name="sizing">autosize</property> | ||
151 | <property name="title" translatable="yes">Size</property> | ||
152 | <property name="sort_column_id">1</property> | ||
153 | <child> | ||
154 | <object class="GtkCellRendererText" id="package_size_cell"/> | ||
155 | </child> | ||
156 | </object> | ||
157 | </child> | ||
158 | </object> | ||
159 | </child> | ||
160 | </object> | ||
161 | <packing> | ||
162 | <property name="resize">False</property> | ||
163 | <property name="shrink">True</property> | ||
164 | </packing> | ||
165 | </child> | ||
166 | <child> | ||
167 | <object class="GtkBox" id="box2"> | ||
168 | <property name="visible">True</property> | ||
169 | <property name="can_focus">False</property> | ||
170 | <property name="margin_left">4</property> | ||
171 | <property name="orientation">vertical</property> | ||
172 | <property name="spacing">4</property> | ||
173 | <child> | ||
174 | <object class="GtkLabel" id="label1"> | ||
175 | <property name="visible">True</property> | ||
176 | <property name="can_focus">False</property> | ||
177 | <property name="xalign">0</property> | ||
178 | <property name="label" translatable="yes">label</property> | ||
179 | </object> | ||
180 | <packing> | ||
181 | <property name="expand">False</property> | ||
182 | <property name="fill">True</property> | ||
183 | <property name="position">0</property> | ||
184 | </packing> | ||
185 | </child> | ||
186 | <child> | ||
187 | <object class="GtkLabel" id="depends_label"> | ||
188 | <property name="visible">True</property> | ||
189 | <property name="can_focus">False</property> | ||
190 | <property name="xalign">0</property> | ||
191 | <property name="label" translatable="yes">depends_label</property> | ||
192 | <property name="wrap">True</property> | ||
193 | <property name="track_visited_links">False</property> | ||
194 | </object> | ||
195 | <packing> | ||
196 | <property name="expand">False</property> | ||
197 | <property name="fill">True</property> | ||
198 | <property name="position">1</property> | ||
199 | </packing> | ||
200 | </child> | ||
201 | <child> | ||
202 | <object class="GtkLabel" id="recommends_label"> | ||
203 | <property name="visible">True</property> | ||
204 | <property name="can_focus">False</property> | ||
205 | <property name="xalign">0</property> | ||
206 | <property name="label" translatable="yes">recs_label</property> | ||
207 | <property name="wrap">True</property> | ||
208 | <property name="track_visited_links">False</property> | ||
209 | </object> | ||
210 | <packing> | ||
211 | <property name="expand">False</property> | ||
212 | <property name="fill">True</property> | ||
213 | <property name="position">2</property> | ||
214 | </packing> | ||
215 | </child> | ||
216 | <child> | ||
217 | <object class="GtkLabel" id="suggests_label"> | ||
218 | <property name="visible">True</property> | ||
219 | <property name="can_focus">False</property> | ||
220 | <property name="xalign">0</property> | ||
221 | <property name="label" translatable="yes">suggests_label</property> | ||
222 | <property name="wrap">True</property> | ||
223 | <property name="track_visited_links">False</property> | ||
224 | </object> | ||
225 | <packing> | ||
226 | <property name="expand">False</property> | ||
227 | <property name="fill">True</property> | ||
228 | <property name="position">3</property> | ||
229 | </packing> | ||
230 | </child> | ||
231 | <child> | ||
232 | <object class="GtkLabel" id="provides_label"> | ||
233 | <property name="visible">True</property> | ||
234 | <property name="can_focus">False</property> | ||
235 | <property name="xalign">0</property> | ||
236 | <property name="label" translatable="yes">provides_label</property> | ||
237 | <property name="wrap">True</property> | ||
238 | <property name="track_visited_links">False</property> | ||
239 | </object> | ||
240 | <packing> | ||
241 | <property name="expand">False</property> | ||
242 | <property name="fill">True</property> | ||
243 | <property name="position">4</property> | ||
244 | </packing> | ||
245 | </child> | ||
246 | <child> | ||
247 | <object class="GtkLabel" id="files_label"> | ||
248 | <property name="visible">True</property> | ||
249 | <property name="can_focus">False</property> | ||
250 | <property name="xalign">0</property> | ||
251 | <property name="label" translatable="yes">files_label</property> | ||
252 | <property name="ellipsize">end</property> | ||
253 | </object> | ||
254 | <packing> | ||
255 | <property name="expand">False</property> | ||
256 | <property name="fill">True</property> | ||
257 | <property name="position">5</property> | ||
258 | </packing> | ||
259 | </child> | ||
260 | <child> | ||
261 | <object class="GtkScrolledWindow" id="files_scrollview"> | ||
262 | <property name="visible">True</property> | ||
263 | <property name="can_focus">True</property> | ||
264 | <property name="shadow_type">in</property> | ||
265 | <child> | ||
266 | <object class="GtkTreeView" id="files_view"> | ||
267 | <property name="visible">True</property> | ||
268 | <property name="can_focus">True</property> | ||
269 | <property name="model">file_store</property> | ||
270 | <property name="rules_hint">True</property> | ||
271 | <property name="search_column">0</property> | ||
272 | <property name="show_expanders">False</property> | ||
273 | <child internal-child="selection"> | ||
274 | <object class="GtkTreeSelection" id="treeview-selection3"/> | ||
275 | </child> | ||
276 | <child> | ||
277 | <object class="GtkTreeViewColumn" id="file_name_column"> | ||
278 | <property name="title" translatable="yes">Name</property> | ||
279 | <property name="sort_indicator">True</property> | ||
280 | <property name="sort_column_id">0</property> | ||
281 | <child> | ||
282 | <object class="GtkCellRendererText" id="cellrenderertext3"> | ||
283 | <property name="background_rgba">rgba(0,0,0,0)</property> | ||
284 | </object> | ||
285 | <attributes> | ||
286 | <attribute name="text">0</attribute> | ||
287 | </attributes> | ||
288 | </child> | ||
289 | </object> | ||
290 | </child> | ||
291 | <child> | ||
292 | <object class="GtkTreeViewColumn" id="treeviewcolumn4"> | ||
293 | <property name="title" translatable="yes">Size</property> | ||
294 | <property name="sort_indicator">True</property> | ||
295 | <property name="sort_column_id">1</property> | ||
296 | <child> | ||
297 | <object class="GtkCellRendererText" id="cellrenderertext4"/> | ||
298 | <attributes> | ||
299 | <attribute name="text">1</attribute> | ||
300 | </attributes> | ||
301 | </child> | ||
302 | </object> | ||
303 | </child> | ||
304 | </object> | ||
305 | </child> | ||
306 | </object> | ||
307 | <packing> | ||
308 | <property name="expand">True</property> | ||
309 | <property name="fill">True</property> | ||
310 | <property name="position">6</property> | ||
311 | </packing> | ||
312 | </child> | ||
313 | </object> | ||
314 | <packing> | ||
315 | <property name="resize">True</property> | ||
316 | <property name="shrink">True</property> | ||
317 | </packing> | ||
318 | </child> | ||
319 | </object> | ||
320 | <packing> | ||
321 | <property name="resize">True</property> | ||
322 | <property name="shrink">True</property> | ||
323 | </packing> | ||
324 | </child> | ||
325 | </object> | ||
326 | <packing> | ||
327 | <property name="expand">True</property> | ||
328 | <property name="fill">True</property> | ||
329 | <property name="position">1</property> | ||
330 | </packing> | ||
331 | </child> | ||
332 | </object> | ||
333 | </child> | ||
334 | </object> | ||
335 | </interface> | ||
diff --git a/scripts/pkgdataui.py b/scripts/pkgdataui.py new file mode 100755 index 0000000000..f6b23fc0be --- /dev/null +++ b/scripts/pkgdataui.py | |||
@@ -0,0 +1,241 @@ | |||
1 | #! /usr/bin/env python3 | ||
2 | |||
3 | import os, sys, enum, ast | ||
4 | |||
5 | scripts_path = os.path.dirname(os.path.realpath(__file__)) | ||
6 | lib_path = scripts_path + '/lib' | ||
7 | sys.path = sys.path + [lib_path] | ||
8 | |||
9 | import scriptpath | ||
10 | bitbakepath = scriptpath.add_bitbake_lib_path() | ||
11 | if not bitbakepath: | ||
12 | print("Unable to find bitbake by searching parent directory of this script or PATH") | ||
13 | sys.exit(1) | ||
14 | import bb | ||
15 | |||
16 | import gi | ||
17 | gi.require_version('Gtk', '3.0') | ||
18 | from gi.repository import Gtk, Gdk, GObject | ||
19 | |||
20 | RecipeColumns = enum.IntEnum("RecipeColumns", {"Recipe": 0}) | ||
21 | PackageColumns = enum.IntEnum("PackageColumns", {"Package": 0, "Size": 1}) | ||
22 | FileColumns = enum.IntEnum("FileColumns", {"Filename": 0, "Size": 1}) | ||
23 | |||
24 | import time | ||
25 | def timeit(f): | ||
26 | def timed(*args, **kw): | ||
27 | ts = time.time() | ||
28 | print ("func:%r calling" % f.__name__) | ||
29 | result = f(*args, **kw) | ||
30 | te = time.time() | ||
31 | print ('func:%r args:[%r, %r] took: %2.4f sec' % \ | ||
32 | (f.__name__, args, kw, te-ts)) | ||
33 | return result | ||
34 | return timed | ||
35 | |||
36 | def human_size(nbytes): | ||
37 | import math | ||
38 | suffixes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'] | ||
39 | human = nbytes | ||
40 | rank = 0 | ||
41 | if nbytes != 0: | ||
42 | rank = int((math.log10(nbytes)) / 3) | ||
43 | rank = min(rank, len(suffixes) - 1) | ||
44 | human = nbytes / (1000.0 ** rank) | ||
45 | f = ('%.2f' % human).rstrip('0').rstrip('.') | ||
46 | return '%s %s' % (f, suffixes[rank]) | ||
47 | |||
48 | def load(filename, suffix=None): | ||
49 | from configparser import ConfigParser | ||
50 | from itertools import chain | ||
51 | |||
52 | parser = ConfigParser() | ||
53 | if suffix: | ||
54 | parser.optionxform = lambda option: option.replace("_" + suffix, "") | ||
55 | with open(filename) as lines: | ||
56 | lines = chain(("[fake]",), lines) | ||
57 | parser.read_file(lines) | ||
58 | |||
59 | # TODO extract the data and put it into a real dict so we can transform some | ||
60 | # values to ints? | ||
61 | return parser["fake"] | ||
62 | |||
63 | def find_pkgdata(): | ||
64 | import subprocess | ||
65 | output = subprocess.check_output(("bitbake", "-e"), universal_newlines=True) | ||
66 | for line in output.splitlines(): | ||
67 | if line.startswith("PKGDATA_DIR="): | ||
68 | return line.split("=", 1)[1].strip("\'\"") | ||
69 | # TODO exception or something | ||
70 | return None | ||
71 | |||
72 | def packages_in_recipe(pkgdata, recipe): | ||
73 | """ | ||
74 | Load the recipe pkgdata to determine the list of runtime packages. | ||
75 | """ | ||
76 | data = load(os.path.join(pkgdata, recipe)) | ||
77 | packages = data["PACKAGES"].split() | ||
78 | return packages | ||
79 | |||
80 | def load_runtime_package(pkgdata, package): | ||
81 | return load(os.path.join(pkgdata, "runtime", package), suffix=package) | ||
82 | |||
83 | def recipe_from_package(pkgdata, package): | ||
84 | data = load(os.path.join(pkgdata, "runtime", package), suffix=package) | ||
85 | return data["PN"] | ||
86 | |||
87 | def summary(data): | ||
88 | s = "" | ||
89 | s += "{0[PKG]} {0[PKGV]}-{0[PKGR]}\n{0[LICENSE]}\n{0[SUMMARY]}\n".format(data) | ||
90 | |||
91 | return s | ||
92 | |||
93 | |||
94 | class PkgUi(): | ||
95 | def __init__(self, pkgdata): | ||
96 | self.pkgdata = pkgdata | ||
97 | self.current_recipe = None | ||
98 | self.recipe_iters = {} | ||
99 | self.package_iters = {} | ||
100 | |||
101 | builder = Gtk.Builder() | ||
102 | builder.add_from_file(os.path.join(os.path.dirname(__file__), "pkgdataui.glade")) | ||
103 | |||
104 | self.window = builder.get_object("window") | ||
105 | self.window.connect("delete-event", Gtk.main_quit) | ||
106 | |||
107 | self.recipe_store = builder.get_object("recipe_store") | ||
108 | self.recipe_view = builder.get_object("recipe_view") | ||
109 | self.package_store = builder.get_object("package_store") | ||
110 | self.package_view = builder.get_object("package_view") | ||
111 | |||
112 | # Somehow resizable does not get set via builder xml | ||
113 | package_name_column = builder.get_object("package_name_column") | ||
114 | package_name_column.set_resizable(True) | ||
115 | file_name_column = builder.get_object("file_name_column") | ||
116 | file_name_column.set_resizable(True) | ||
117 | |||
118 | self.recipe_view.get_selection().connect("changed", self.on_recipe_changed) | ||
119 | self.package_view.get_selection().connect("changed", self.on_package_changed) | ||
120 | |||
121 | self.package_store.set_sort_column_id(PackageColumns.Package, Gtk.SortType.ASCENDING) | ||
122 | builder.get_object("package_size_column").set_cell_data_func(builder.get_object("package_size_cell"), lambda column, cell, model, iter, data: cell.set_property("text", human_size(model[iter][PackageColumns.Size]))) | ||
123 | |||
124 | self.label = builder.get_object("label1") | ||
125 | self.depends_label = builder.get_object("depends_label") | ||
126 | self.recommends_label = builder.get_object("recommends_label") | ||
127 | self.suggests_label = builder.get_object("suggests_label") | ||
128 | self.provides_label = builder.get_object("provides_label") | ||
129 | |||
130 | self.depends_label.connect("activate-link", self.on_link_activate) | ||
131 | self.recommends_label.connect("activate-link", self.on_link_activate) | ||
132 | self.suggests_label.connect("activate-link", self.on_link_activate) | ||
133 | |||
134 | self.file_store = builder.get_object("file_store") | ||
135 | self.file_store.set_sort_column_id(FileColumns.Filename, Gtk.SortType.ASCENDING) | ||
136 | self.files_view = builder.get_object("files_scrollview") | ||
137 | self.files_label = builder.get_object("files_label") | ||
138 | |||
139 | self.load_recipes() | ||
140 | |||
141 | self.recipe_view.set_cursor(Gtk.TreePath.new_first()) | ||
142 | |||
143 | self.window.show() | ||
144 | |||
145 | def on_link_activate(self, label, url_string): | ||
146 | from urllib.parse import urlparse | ||
147 | url = urlparse(url_string) | ||
148 | if url.scheme == "package": | ||
149 | package = url.path | ||
150 | recipe = recipe_from_package(self.pkgdata, package) | ||
151 | |||
152 | it = self.recipe_iters[recipe] | ||
153 | path = self.recipe_store.get_path(it) | ||
154 | self.recipe_view.set_cursor(path) | ||
155 | self.recipe_view.scroll_to_cell(path) | ||
156 | |||
157 | self.on_recipe_changed(self.recipe_view.get_selection()) | ||
158 | |||
159 | it = self.package_iters[package] | ||
160 | path = self.package_store.get_path(it) | ||
161 | self.package_view.set_cursor(path) | ||
162 | self.package_view.scroll_to_cell(path) | ||
163 | |||
164 | return True | ||
165 | else: | ||
166 | return False | ||
167 | |||
168 | def on_recipe_changed(self, selection): | ||
169 | self.package_store.clear() | ||
170 | self.package_iters = {} | ||
171 | |||
172 | (model, it) = selection.get_selected() | ||
173 | if not it: | ||
174 | return | ||
175 | |||
176 | recipe = model[it][RecipeColumns.Recipe] | ||
177 | for package in packages_in_recipe(self.pkgdata, recipe): | ||
178 | # TODO also show PKG after debian-renaming? | ||
179 | data = load_runtime_package(self.pkgdata, package) | ||
180 | # TODO stash data to avoid reading in on_package_changed | ||
181 | self.package_iters[package] = self.package_store.append([package, int(data["PKGSIZE"])]) | ||
182 | |||
183 | def on_package_changed(self, selection): | ||
184 | self.label.set_text("") | ||
185 | self.file_store.clear() | ||
186 | self.depends_label.hide() | ||
187 | self.recommends_label.hide() | ||
188 | self.suggests_label.hide() | ||
189 | |||
190 | (model, it) = selection.get_selected() | ||
191 | if it is None: | ||
192 | return | ||
193 | |||
194 | package = model[it][PackageColumns.Package] | ||
195 | data = load_runtime_package(self.pkgdata, package) | ||
196 | |||
197 | self.label.set_text(summary(data)) | ||
198 | |||
199 | files = ast.literal_eval(data["FILES_INFO"]) | ||
200 | if files: | ||
201 | self.files_label.set_text("{0} files take {1}.".format(len(files), human_size(int(data["PKGSIZE"])))) | ||
202 | self.files_view.show() | ||
203 | for filename, size in files.items(): | ||
204 | self.file_store.append([filename, size]) | ||
205 | else: | ||
206 | self.files_view.hide() | ||
207 | self.files_label.set_text("This package has no files.") | ||
208 | |||
209 | def update_deps(field, prefix, label, clickable=True): | ||
210 | if field in data: | ||
211 | l = [] | ||
212 | for name, version in bb.utils.explode_dep_versions2(data[field]).items(): | ||
213 | if clickable: | ||
214 | l.append("<a href='package:{0}'>{0}</a> {1}".format(name, " ".join(version))) | ||
215 | else: | ||
216 | l.append("{0} {1}".format(name, " ".join(version))) | ||
217 | label.set_markup(prefix + ", ".join(l)) | ||
218 | label.show() | ||
219 | else: | ||
220 | label.hide() | ||
221 | update_deps("RDEPENDS", "Depends: ", self.depends_label) | ||
222 | update_deps("RRECOMMENDS", "Recommends: ", self.recommends_label) | ||
223 | update_deps("RSUGGESTS", "Suggests: ", self.suggests_label) | ||
224 | update_deps("RPROVIDES", "Provides: ", self.provides_label, clickable=False) | ||
225 | |||
226 | def load_recipes(self): | ||
227 | for recipe in sorted(os.listdir(pkgdata)): | ||
228 | if os.path.isfile(os.path.join(pkgdata, recipe)): | ||
229 | self.recipe_iters[recipe] = self.recipe_store.append([recipe]) | ||
230 | |||
231 | if __name__ == "__main__": | ||
232 | import argparse | ||
233 | |||
234 | parser = argparse.ArgumentParser(description='pkgdata browser') | ||
235 | parser.add_argument('-p', '--pkgdata', help="Optional location of pkgdata") | ||
236 | |||
237 | args = parser.parse_args() | ||
238 | pkgdata = args.pkgdata if args.pkgdata else find_pkgdata() | ||
239 | # TODO assert pkgdata is a directory | ||
240 | window = PkgUi(pkgdata) | ||
241 | Gtk.main() | ||