diff options
author | Darren Hart <dvhltc@us.ibm.com> | 2010-01-04 20:24:41 -0500 |
---|---|---|
committer | Steven Rostedt <rostedt@goodmis.org> | 2010-01-04 23:40:02 -0500 |
commit | bbb314602b12c2a2d9f859f75576c4f3aa58f306 (patch) | |
tree | 4d5713041061bef2c887fb8429ae06c2ace08495 | |
parent | f18df1531e4da5685dd7441811c57ba96af0c055 (diff) |
trace-view: Provide GTK TreeModel trace-view-store available to python
Make the necessary trace-view-store methods non-static and place their
declarations in the header file.
Functions beginning with _ are considered private in python. The structs
beginning with _ caused their accessor functions to be generated as
private. Since they aren't used as structs anywhere in the C code,
rename them to something more accessible to python.
Signed-off-by: Darren Hart <dvhltc@us.ibm.com>
LKML-Reference: <1262654682-20325-3-git-send-email-dvhltc@us.ibm.com>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
-rw-r--r-- | Makefile | 7 | ||||
-rw-r--r-- | ctracecmdgui.i | 78 | ||||
-rw-r--r-- | trace-view-store.c | 23 | ||||
-rw-r--r-- | trace-view-store.h | 25 | ||||
-rw-r--r-- | tracecmdgui.py | 239 |
5 files changed, 348 insertions, 24 deletions
@@ -137,6 +137,13 @@ python: $(TCMD_LIB_OBJS) | |||
137 | $(CC) --shared $^ ctracecmd_wrap.o -o ctracecmd.so | 137 | $(CC) --shared $^ ctracecmd_wrap.o -o ctracecmd.so |
138 | #$(CC) --shared $^ ctracecmd_wrap.o -o _ctracecmd.so | 138 | #$(CC) --shared $^ ctracecmd_wrap.o -o _ctracecmd.so |
139 | 139 | ||
140 | .PHONY: python-gui | ||
141 | python-gui: $(TRACE_VIEW_OBJS) | ||
142 | swig -Wall -python -noproxy ctracecmdgui.i | ||
143 | # FIXME: where do we get the pygtk include from? | ||
144 | gcc -fpic -c `python-config --includes` $(CFLAGS) $(INCLUDES) -I/usr/include/pygtk-2.0/ ctracecmdgui_wrap.c | ||
145 | $(CC) --shared $^ $(LIBS) $(CONFIG_LIBS) ctracecmdgui_wrap.o -o ctracecmdgui.so | ||
146 | |||
140 | 147 | ||
141 | .PHONY: force | 148 | .PHONY: force |
142 | force: | 149 | force: |
diff --git a/ctracecmdgui.i b/ctracecmdgui.i new file mode 100644 index 0000000..1dcdab0 --- /dev/null +++ b/ctracecmdgui.i | |||
@@ -0,0 +1,78 @@ | |||
1 | // ctracecmdgui.i | ||
2 | %module ctracecmdgui | ||
3 | %include typemaps.i | ||
4 | |||
5 | %{ | ||
6 | #include "trace-view-store.h" | ||
7 | #include <pygobject.h> | ||
8 | #include <pyglib.h> | ||
9 | #include <Python.h> | ||
10 | |||
11 | extern GtkTreeModel *trace_view_store_as_gtk_tree_model(struct trace_view_store *store); | ||
12 | |||
13 | PyObject * | ||
14 | pytype_from_gtype(GType gtype) | ||
15 | { | ||
16 | PyTypeObject *pt = NULL; | ||
17 | switch (gtype) { | ||
18 | case G_TYPE_INT: | ||
19 | case G_TYPE_UINT: | ||
20 | pt = &PyLong_Type; | ||
21 | break; | ||
22 | case G_TYPE_STRING: | ||
23 | pt = &PyUnicode_Type; | ||
24 | break; | ||
25 | default: | ||
26 | return Py_None; | ||
27 | } | ||
28 | return (PyObject *)pt; | ||
29 | } | ||
30 | %} | ||
31 | |||
32 | |||
33 | /* return python longs from unsigned long long functions */ | ||
34 | %typemap(out) unsigned long long { | ||
35 | $result = PyLong_FromUnsignedLongLong((unsigned long long) $1); | ||
36 | } | ||
37 | |||
38 | /* help swig cope with g* types */ | ||
39 | %typemap(in) gint { | ||
40 | $1 = PyInt_AsLong($input); | ||
41 | } | ||
42 | %typemap(out) gint { | ||
43 | $result = PyInt_FromLong($1); | ||
44 | } | ||
45 | %typemap(in) guint { | ||
46 | $1 = PyLong_AsUnsignedLong($input); | ||
47 | } | ||
48 | %typemap(out) guint { | ||
49 | $result = PyLong_FromUnsignedLong($1); | ||
50 | } | ||
51 | %typemap(in) guint64 { | ||
52 | $1 = PyLong_AsUnsignedLongLong($input); | ||
53 | } | ||
54 | %typemap(out) guint64 { | ||
55 | $result = PyLong_FromUnsignedLongLong($1); | ||
56 | } | ||
57 | %typemap(out) GType { | ||
58 | $result = pytype_from_gtype($1); | ||
59 | } | ||
60 | %typemap(out) GtkTreeModelFlags { | ||
61 | $result = PyLong_FromLong($1); | ||
62 | } | ||
63 | |||
64 | |||
65 | %inline %{ | ||
66 | GtkTreeModel *trace_view_store_as_gtk_tree_model(struct trace_view_store *store) | ||
67 | { | ||
68 | return GTK_TREE_MODEL(store); | ||
69 | } | ||
70 | %} | ||
71 | |||
72 | |||
73 | /* SWIG can't grok these, define them to nothing */ | ||
74 | #define __trace | ||
75 | #define __attribute__(x) | ||
76 | #define __thread | ||
77 | |||
78 | %include "trace-view-store.h" | ||
diff --git a/trace-view-store.c b/trace-view-store.c index b167cc9..03664c8 100644 --- a/trace-view-store.c +++ b/trace-view-store.c | |||
@@ -13,25 +13,13 @@ static void trace_view_store_tree_model_init (GtkTreeModelIface *iface); | |||
13 | 13 | ||
14 | static void trace_view_store_finalize (GObject *object); | 14 | static void trace_view_store_finalize (GObject *object); |
15 | 15 | ||
16 | static GtkTreeModelFlags trace_view_store_get_flags (GtkTreeModel *tree_model); | ||
17 | |||
18 | static gint trace_view_store_get_n_columns (GtkTreeModel *tree_model); | ||
19 | |||
20 | static GType trace_view_store_get_column_type (GtkTreeModel *tree_model, | ||
21 | gint index); | ||
22 | |||
23 | static gboolean trace_view_store_get_iter (GtkTreeModel *tree_model, | 16 | static gboolean trace_view_store_get_iter (GtkTreeModel *tree_model, |
24 | GtkTreeIter *iter, | 17 | GtkTreeIter *iter, |
25 | GtkTreePath *path); | 18 | GtkTreePath *path); |
26 | 19 | ||
27 | static GtkTreePath *trace_view_store_get_path (GtkTreeModel *tree_model, | 20 | static GtkTreePath *trace_view_store_get_path (GtkTreeModel *tree_model, |
28 | GtkTreeIter *iter); | 21 | GtkTreeIter *iter); |
29 | 22 | ||
30 | static void trace_view_store_get_value (GtkTreeModel *tree_model, | ||
31 | GtkTreeIter *iter, | ||
32 | gint column, | ||
33 | GValue *value); | ||
34 | |||
35 | static gboolean trace_view_store_iter_next (GtkTreeModel *tree_model, | 23 | static gboolean trace_view_store_iter_next (GtkTreeModel *tree_model, |
36 | GtkTreeIter *iter); | 24 | GtkTreeIter *iter); |
37 | 25 | ||
@@ -55,7 +43,6 @@ static gboolean trace_view_store_iter_parent (GtkTreeModel *tree_model, | |||
55 | GtkTreeIter *child); | 43 | GtkTreeIter *child); |
56 | 44 | ||
57 | 45 | ||
58 | |||
59 | static GObjectClass *parent_class = NULL; /* GObject stuff - nothing to worry about */ | 46 | static GObjectClass *parent_class = NULL; /* GObject stuff - nothing to worry about */ |
60 | 47 | ||
61 | 48 | ||
@@ -240,7 +227,7 @@ trace_view_store_finalize (GObject *object) | |||
240 | * | 227 | * |
241 | *****************************************************************************/ | 228 | *****************************************************************************/ |
242 | 229 | ||
243 | static GtkTreeModelFlags | 230 | GtkTreeModelFlags |
244 | trace_view_store_get_flags (GtkTreeModel *tree_model) | 231 | trace_view_store_get_flags (GtkTreeModel *tree_model) |
245 | { | 232 | { |
246 | g_return_val_if_fail (TRACE_VIEW_IS_LIST(tree_model), (GtkTreeModelFlags)0); | 233 | g_return_val_if_fail (TRACE_VIEW_IS_LIST(tree_model), (GtkTreeModelFlags)0); |
@@ -256,7 +243,7 @@ trace_view_store_get_flags (GtkTreeModel *tree_model) | |||
256 | * | 243 | * |
257 | *****************************************************************************/ | 244 | *****************************************************************************/ |
258 | 245 | ||
259 | static gint | 246 | gint |
260 | trace_view_store_get_n_columns (GtkTreeModel *tree_model) | 247 | trace_view_store_get_n_columns (GtkTreeModel *tree_model) |
261 | { | 248 | { |
262 | g_return_val_if_fail (TRACE_VIEW_IS_LIST(tree_model), 0); | 249 | g_return_val_if_fail (TRACE_VIEW_IS_LIST(tree_model), 0); |
@@ -299,7 +286,7 @@ static gint get_visible_column(TraceViewStore *trace_view, gint column) | |||
299 | * | 286 | * |
300 | *****************************************************************************/ | 287 | *****************************************************************************/ |
301 | 288 | ||
302 | static GType | 289 | GType |
303 | trace_view_store_get_column_type (GtkTreeModel *tree_model, | 290 | trace_view_store_get_column_type (GtkTreeModel *tree_model, |
304 | gint index) | 291 | gint index) |
305 | { | 292 | { |
@@ -394,7 +381,7 @@ trace_view_store_get_path (GtkTreeModel *tree_model, | |||
394 | * | 381 | * |
395 | *****************************************************************************/ | 382 | *****************************************************************************/ |
396 | 383 | ||
397 | static void | 384 | void |
398 | trace_view_store_get_value (GtkTreeModel *tree_model, | 385 | trace_view_store_get_value (GtkTreeModel *tree_model, |
399 | GtkTreeIter *iter, | 386 | GtkTreeIter *iter, |
400 | gint column, | 387 | gint column, |
diff --git a/trace-view-store.h b/trace-view-store.h index 53e0b37..bfa19d9 100644 --- a/trace-view-store.h +++ b/trace-view-store.h | |||
@@ -31,15 +31,15 @@ enum | |||
31 | } ; | 31 | } ; |
32 | 32 | ||
33 | 33 | ||
34 | typedef struct _TraceViewRecord TraceViewRecord; | 34 | typedef struct trace_view_record TraceViewRecord; |
35 | typedef struct _TraceViewStore TraceViewStore; | 35 | typedef struct trace_view_store TraceViewStore; |
36 | typedef struct _TraceViewStoreClass TraceViewStoreClass; | 36 | typedef struct trace_view_store_class TraceViewStoreClass; |
37 | 37 | ||
38 | 38 | ||
39 | 39 | ||
40 | /* TraceViewRecord: this structure represents a row */ | 40 | /* TraceViewRecord: this structure represents a row */ |
41 | 41 | ||
42 | struct _TraceViewRecord | 42 | struct trace_view_record |
43 | { | 43 | { |
44 | /* What we need from the record */ | 44 | /* What we need from the record */ |
45 | guint64 timestamp; | 45 | guint64 timestamp; |
@@ -60,7 +60,7 @@ struct _TraceViewRecord | |||
60 | * crucial that 'parent' is the first member of the | 60 | * crucial that 'parent' is the first member of the |
61 | * structure. */ | 61 | * structure. */ |
62 | 62 | ||
63 | struct _TraceViewStore | 63 | struct trace_view_store |
64 | { | 64 | { |
65 | GObject parent; /* this MUST be the first member */ | 65 | GObject parent; /* this MUST be the first member */ |
66 | 66 | ||
@@ -125,9 +125,22 @@ void trace_view_store_filter_tasks(TraceViewStore *store, struct filter_task *fi | |||
125 | 125 | ||
126 | TraceViewRecord *trace_view_store_get_row(TraceViewStore *store, gint row); | 126 | TraceViewRecord *trace_view_store_get_row(TraceViewStore *store, gint row); |
127 | 127 | ||
128 | /* TraceViewStore methos */ | ||
129 | GtkTreeModelFlags trace_view_store_get_flags (GtkTreeModel *tree_model); | ||
130 | |||
131 | gint trace_view_store_get_n_columns (GtkTreeModel *tree_model); | ||
132 | |||
133 | GType trace_view_store_get_column_type (GtkTreeModel *tree_model, | ||
134 | gint index); | ||
135 | |||
136 | void trace_view_store_get_value (GtkTreeModel *tree_model, | ||
137 | GtkTreeIter *iter, | ||
138 | gint column, | ||
139 | GValue *value); | ||
140 | |||
128 | /* TraceViewStoreClass: more boilerplate GObject stuff */ | 141 | /* TraceViewStoreClass: more boilerplate GObject stuff */ |
129 | 142 | ||
130 | struct _TraceViewStoreClass | 143 | struct trace_view_store_class |
131 | { | 144 | { |
132 | GObjectClass parent_class; | 145 | GObjectClass parent_class; |
133 | }; | 146 | }; |
diff --git a/tracecmdgui.py b/tracecmdgui.py new file mode 100644 index 0000000..63400b0 --- /dev/null +++ b/tracecmdgui.py | |||
@@ -0,0 +1,239 @@ | |||
1 | # | ||
2 | # Copyright (C) International Business Machines Corp., 2009 | ||
3 | # | ||
4 | # This program is free software; you can redistribute it and/or modify | ||
5 | # it under the terms of the GNU General Public License as published by | ||
6 | # the Free Software Foundation; either version 2 of the License, or | ||
7 | # (at your option) any later version. | ||
8 | # | ||
9 | # This program is distributed in the hope that it will be useful, | ||
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | # GNU General Public License for more details. | ||
13 | # | ||
14 | # You should have received a copy of the GNU General Public License | ||
15 | # along with this program; if not, write to the Free Software | ||
16 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | # | ||
18 | # 2009-Dec-31: Initial version by Darren Hart <dvhltc@us.ibm.com> | ||
19 | # | ||
20 | |||
21 | import gobject #delete me ? | ||
22 | import time | ||
23 | import sys | ||
24 | import gtk | ||
25 | from tracecmd import * | ||
26 | from ctracecmdgui import * | ||
27 | |||
28 | """ | ||
29 | Python interface for tracecmd GTK widgets | ||
30 | |||
31 | Python tracecmd applications should be written to this interface. It will be | ||
32 | updated as the tracecmd gui C API changes and try to minimze the impact to | ||
33 | python applications. The ctracecmdgui Python module is automatically generated | ||
34 | using SWIG and it is recommended applications not use it directly. | ||
35 | """ | ||
36 | |||
37 | # In a "real" app these width should be determined at runtime testing max length | ||
38 | # strings in the current font. | ||
39 | TS_COL_W = 150 | ||
40 | CPU_COL_W = 35 | ||
41 | EVENT_COL_W = 150 | ||
42 | PID_COL_W = 75 | ||
43 | COMM_COL_W = 250 | ||
44 | |||
45 | |||
46 | def timing(func): | ||
47 | def wrapper(*arg): | ||
48 | start = time.time() | ||
49 | ret = func(*arg) | ||
50 | end = time.time() | ||
51 | print '@%s took %0.3f s' % (func.func_name, (end-start)) | ||
52 | return ret | ||
53 | return wrapper | ||
54 | |||
55 | |||
56 | class EventStore(gtk.GenericTreeModel): | ||
57 | # FIXME: get these from the C code: trace_view_store->column_types ... | ||
58 | @timing | ||
59 | def __init__(self, trace): | ||
60 | gtk.GenericTreeModel.__init__(self) | ||
61 | self.trace = trace | ||
62 | self.cstore = trace_view_store_new(trace.handle) | ||
63 | self.gtk_cstore = trace_view_store_as_gtk_tree_model(self.cstore) | ||
64 | num_rows = trace_view_store_num_rows_get(self.cstore) | ||
65 | print "Loaded %d events from trace" % (num_rows) | ||
66 | |||
67 | def on_get_flags(self): | ||
68 | return trace_view_store_get_flags(self.gtk_cstore) | ||
69 | |||
70 | def on_get_n_columns(self): | ||
71 | return trace_view_store_get_n_columns(self.gtk_cstore) | ||
72 | |||
73 | def on_get_column_type(self, col): | ||
74 | # I couldn't figure out how to convert the C GType into the python | ||
75 | # GType. The current typemap converts the C GType into the python type, | ||
76 | # which is what this function is supposed to return anyway. | ||
77 | pytype = trace_view_store_get_column_type(self.gtk_cstore, col) | ||
78 | return pytype | ||
79 | |||
80 | def on_get_iter(self, path): | ||
81 | if len(path) > 1 and path[1] != 1: | ||
82 | return None | ||
83 | n = path[0] | ||
84 | rec = trace_view_store_get_row(self.cstore, n) | ||
85 | return rec | ||
86 | |||
87 | def on_get_path(self, rec): | ||
88 | if not rec: | ||
89 | return None | ||
90 | start_row = trace_view_store_start_row_get(self.cstore) | ||
91 | return (trace_view_record_pos_get(rec) - start_row,) | ||
92 | |||
93 | def on_get_value(self, rec, col): | ||
94 | # FIXME: write SWIG wrapper to marshal the Gvalue and wrap the rec in an | ||
95 | # Iter | ||
96 | pass | ||
97 | #return trace_view_store_get_value_py(self.cstore, rec, col) | ||
98 | |||
99 | def on_iter_next(self, rec): | ||
100 | pos = trace_view_record_pos_get(rec) | ||
101 | start_row = trace_view_store_start_row_get(self.cstore) | ||
102 | return trace_view_store_get_row(self.cstore, pos - start_row + 1) | ||
103 | |||
104 | def on_iter_children(self, rec): | ||
105 | if rec: | ||
106 | return None | ||
107 | return trace_view_store_get_row(self.cstore, 0) | ||
108 | |||
109 | def on_iter_has_child(self, rec): | ||
110 | return False | ||
111 | |||
112 | def on_iter_n_children(self, rec): | ||
113 | if rec: | ||
114 | return 0 | ||
115 | return trace_view_store_num_rows_get(self.cstore) | ||
116 | |||
117 | def on_iter_nth_child(self, rec, n): | ||
118 | if rec: | ||
119 | return None | ||
120 | return trace_view_store_get_row(self.cstore, n) | ||
121 | |||
122 | def on_iter_parent(self, child): | ||
123 | return None | ||
124 | |||
125 | def get_event(self, iter): | ||
126 | path = self.get_path(iter) | ||
127 | if not path: | ||
128 | return None | ||
129 | rec = trace_view_store_get_row(self.cstore, path[0]) | ||
130 | if not rec: | ||
131 | return None | ||
132 | ev = self.trace.read_event_at(trace_view_record_offset_get(rec)) | ||
133 | return ev | ||
134 | |||
135 | |||
136 | class EventView(gtk.TreeView): | ||
137 | def __init__(self, model): | ||
138 | gtk.TreeView.__init__(self, model) | ||
139 | self.set_fixed_height_mode(True) | ||
140 | |||
141 | ts_col = gtk.TreeViewColumn("Time (s)") | ||
142 | ts_col.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) | ||
143 | ts_col.set_fixed_width(TS_COL_W) | ||
144 | ts_cell = gtk.CellRendererText() | ||
145 | ts_col.pack_start(ts_cell, False) | ||
146 | ts_col.set_cell_data_func(ts_cell, self.data_func, "ts") | ||
147 | self.append_column(ts_col) | ||
148 | |||
149 | cpu_col = gtk.TreeViewColumn("CPU") | ||
150 | cpu_col.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) | ||
151 | cpu_col.set_fixed_width(CPU_COL_W) | ||
152 | cpu_cell = gtk.CellRendererText() | ||
153 | cpu_col.pack_start(cpu_cell, False) | ||
154 | cpu_col.set_cell_data_func(cpu_cell, self.data_func, "cpu") | ||
155 | self.append_column(cpu_col) | ||
156 | |||
157 | event_col = gtk.TreeViewColumn("Event") | ||
158 | event_col.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) | ||
159 | event_col.set_fixed_width(EVENT_COL_W) | ||
160 | event_cell = gtk.CellRendererText() | ||
161 | event_col.pack_start(event_cell, False) | ||
162 | event_col.set_cell_data_func(event_cell, self.data_func, "event") | ||
163 | self.append_column(event_col) | ||
164 | |||
165 | pid_col = gtk.TreeViewColumn("PID") | ||
166 | pid_col.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) | ||
167 | pid_col.set_fixed_width(PID_COL_W) | ||
168 | pid_cell = gtk.CellRendererText() | ||
169 | pid_col.pack_start(pid_cell, False) | ||
170 | pid_col.set_cell_data_func(pid_cell, self.data_func, "pid") | ||
171 | self.append_column(pid_col) | ||
172 | |||
173 | comm_col = gtk.TreeViewColumn("Comm") | ||
174 | comm_col.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) | ||
175 | comm_col.set_fixed_width(COMM_COL_W) | ||
176 | comm_cell = gtk.CellRendererText() | ||
177 | comm_col.pack_start(comm_cell, False) | ||
178 | comm_col.set_cell_data_func(comm_cell, self.data_func, "comm") | ||
179 | self.append_column(comm_col) | ||
180 | |||
181 | def data_func(self, col, cell, model, iter, data): | ||
182 | ev = model.get_event(iter) | ||
183 | #ev = model.get_value(iter, 0) | ||
184 | if not ev: | ||
185 | return False | ||
186 | |||
187 | if data == "ts": | ||
188 | cell.set_property("markup", "%d.%d" % (ev.ts/1000000000, | ||
189 | ev.ts%1000000000)) | ||
190 | elif data == "cpu": | ||
191 | cell.set_property("markup", ev.cpu) | ||
192 | elif data == "event": | ||
193 | cell.set_property("markup", ev.name) | ||
194 | elif data == "pid": | ||
195 | cell.set_property("markup", ev.pid) | ||
196 | elif data == "comm": | ||
197 | cell.set_property("markup", ev.comm) | ||
198 | else: | ||
199 | print "Unknown Column:", data | ||
200 | return False | ||
201 | |||
202 | return True | ||
203 | |||
204 | |||
205 | class EventViewerApp(gtk.Window): | ||
206 | def __init__(self, trace): | ||
207 | gtk.Window.__init__(self) | ||
208 | |||
209 | self.set_size_request(650, 400) | ||
210 | self.set_position(gtk.WIN_POS_CENTER) | ||
211 | |||
212 | self.connect("destroy", gtk.main_quit) | ||
213 | self.set_title("Event Viewer") | ||
214 | |||
215 | store = EventStore(trace) | ||
216 | view = EventView(store) | ||
217 | |||
218 | sw = gtk.ScrolledWindow() | ||
219 | sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS) | ||
220 | sw.add(view) | ||
221 | |||
222 | # track how often the treeview data_func is called | ||
223 | self.add(sw) | ||
224 | self.show_all() | ||
225 | |||
226 | |||
227 | # Basic builtin test, execute module directly | ||
228 | if __name__ == "__main__": | ||
229 | if len(sys.argv) >=2: | ||
230 | filename = sys.argv[1] | ||
231 | else: | ||
232 | filename = "trace.dat" | ||
233 | |||
234 | print "Initializing trace..." | ||
235 | trace = Trace(filename) | ||
236 | print "Initializing app..." | ||
237 | app = EventViewerApp(trace) | ||
238 | print "Go!" | ||
239 | gtk.main() | ||