From 0b28e42f0e69f29937af9c78add18b7e0ebde16c Mon Sep 17 00:00:00 2001
From: Manuel Kaufmann <humitos@gmail.com>
Date: Wed, 25 Jul 2012 13:11:11 -0300
Subject: [PATCH v2 Log] Port to Gtk3 SL #3761
Convert all the gtk2 and sugar-toolkit code to launch this Activity
using Gtk3.
* Searching on the log file is not working due to this bug:
https://bugzilla.gnome.org/show_bug.cgi?id=680597
All these steps are documented here:
* http://wiki.sugarlabs.org/go/Features/GTK3/Porting/Log
Signed-off-by: Manuel Kaufmann <humitos@gmail.com>
---
logviewer.py | 163 ++++++++++++++++++++++++++++++++--------------------------
setup.py | 2 +-
2 files changed, 92 insertions(+), 73 deletions(-)
diff --git a/logviewer.py b/logviewer.py
index 71ab5c1..02cf25b 100644
a
|
b
|
from gettext import gettext as _ |
22 | 22 | |
23 | 23 | import re |
24 | 24 | |
25 | | import gtk |
26 | | import pango |
27 | | import gobject |
28 | | import gio |
29 | | |
30 | | from sugar.activity import activity |
31 | | from sugar.activity.widgets import ActivityToolbarButton |
32 | | from sugar import env |
33 | | from sugar.graphics import iconentry |
34 | | from sugar.graphics.toolbutton import ToolButton |
35 | | from sugar.graphics.toggletoolbutton import ToggleToolButton |
36 | | from sugar.graphics.palette import Palette |
37 | | from sugar.graphics.alert import NotifyAlert |
| 25 | from gi.repository import Gtk |
| 26 | from gi.repository import Gdk |
| 27 | from gi.repository import Pango |
| 28 | from gi.repository import GObject |
| 29 | from gi.repository import Gio |
| 30 | |
| 31 | from sugar3.activity import activity |
| 32 | from sugar3.activity.widgets import ActivityToolbarButton |
| 33 | from sugar3 import env |
| 34 | from sugar3.graphics import iconentry |
| 35 | from sugar3.graphics.toolbutton import ToolButton |
| 36 | from sugar3.graphics.toggletoolbutton import ToggleToolButton |
| 37 | from sugar3.graphics.palette import Palette |
| 38 | from sugar3.graphics.alert import NotifyAlert |
38 | 39 | from logcollect import LogCollect |
39 | | from sugar.graphics.toolbarbox import ToolbarBox |
40 | | from sugar.activity.widgets import CopyButton, StopButton |
41 | | from sugar.datastore import datastore |
| 40 | from sugar3.graphics.toolbarbox import ToolbarBox |
| 41 | from sugar3.activity.widgets import CopyButton, StopButton |
| 42 | from sugar3.datastore import datastore |
42 | 43 | |
43 | 44 | |
44 | 45 | _AUTOSEARCH_TIMEOUT = 1000 |
… |
… |
def _notify_response_cb(notify, response, activity): |
49 | 50 | activity.remove_alert(notify) |
50 | 51 | |
51 | 52 | |
52 | | class MultiLogView(gtk.HPaned): |
| 53 | class MultiLogView(Gtk.HPaned): |
53 | 54 | |
54 | 55 | def __init__(self, paths, extra_files): |
55 | | gtk.HPaned.__init__(self) |
| 56 | GObject.GObject.__init__(self) |
56 | 57 | |
57 | 58 | self.paths = paths |
58 | 59 | self.extra_files = extra_files |
… |
… |
class MultiLogView(gtk.HPaned): |
73 | 74 | self._find_logs() |
74 | 75 | |
75 | 76 | def _build_treeview(self): |
76 | | self._treeview = gtk.TreeView() |
| 77 | self._treeview = Gtk.TreeView() |
77 | 78 | |
78 | 79 | self._treeview.set_rules_hint(True) |
79 | 80 | self._treeview.connect('cursor-changed', self._cursor_changed_cb) |
80 | 81 | |
81 | | self._treemodel = gtk.TreeStore(gobject.TYPE_STRING) |
| 82 | self._treemodel = Gtk.TreeStore(GObject.TYPE_STRING) |
82 | 83 | |
83 | | sorted = gtk.TreeModelSort(self._treemodel) |
84 | | sorted.set_sort_column_id(0, gtk.SORT_ASCENDING) |
| 84 | # README: https://bugzilla.gnome.org/show_bug.cgi?id=680009 |
| 85 | sorted = self._treemodel.sort_new_with_model() |
| 86 | sorted.set_sort_column_id(0, Gtk.SortType.ASCENDING) |
85 | 87 | sorted.set_sort_func(0, self._sort_logfile) |
86 | 88 | self._treeview.set_model(sorted) |
87 | 89 | |
88 | | renderer = gtk.CellRendererText() |
89 | | col = gtk.TreeViewColumn(_('Log Files'), renderer, text=0) |
| 90 | renderer = Gtk.CellRendererText() |
| 91 | col = Gtk.TreeViewColumn(_('Log Files'), renderer, text=0) |
90 | 92 | self._treeview.append_column(col) |
91 | 93 | |
92 | 94 | self.path_iter = {} |
… |
… |
class MultiLogView(gtk.HPaned): |
96 | 98 | if len(self.extra_files): |
97 | 99 | self.extra_iter = self._treemodel.append(None, [_('Other')]) |
98 | 100 | |
99 | | scroll = gtk.ScrolledWindow() |
100 | | scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) |
| 101 | scroll = Gtk.ScrolledWindow() |
| 102 | scroll.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) |
101 | 103 | scroll.add(self._treeview) |
102 | | scroll.set_size_request(gtk.gdk.screen_width() * 30 / 100, -1) |
| 104 | scroll.set_size_request(Gdk.Screen.width() * 30 / 100, -1) |
103 | 105 | |
104 | 106 | self.add1(scroll) |
105 | 107 | |
106 | 108 | def _build_textview(self): |
107 | | self._textview = gtk.TextView() |
108 | | self._textview.set_wrap_mode(gtk.WRAP_NONE) |
| 109 | self._textview = Gtk.TextView() |
| 110 | self._textview.set_wrap_mode(Gtk.WrapMode.NONE) |
109 | 111 | |
110 | | pangoFont = pango.FontDescription('Mono') |
| 112 | pangoFont = Pango.FontDescription('Mono') |
111 | 113 | self._textview.modify_font(pangoFont) |
112 | 114 | |
113 | | bgcolor = gtk.gdk.color_parse('#FFFFFF') |
114 | | self._textview.modify_base(gtk.STATE_NORMAL, bgcolor) |
| 115 | bgcolor = Gdk.color_parse('#FFFFFF') |
| 116 | self._textview.modify_base(Gtk.StateType.NORMAL, bgcolor) |
115 | 117 | |
116 | 118 | self._textview.set_editable(False) |
117 | 119 | |
118 | | self._tagtable = gtk.TextTagTable() |
119 | | hilite_tag = gtk.TextTag('search-hilite') |
| 120 | self._tagtable = Gtk.TextTagTable() |
| 121 | hilite_tag = Gtk.TextTag.new('search-hilite') |
120 | 122 | hilite_tag.props.background = '#FFFFB0' |
121 | 123 | self._tagtable.add(hilite_tag) |
122 | | select_tag = gtk.TextTag('search-select') |
| 124 | select_tag = Gtk.TextTag.new('search-select') |
123 | 125 | select_tag.props.background = '#B0B0FF' |
124 | 126 | self._tagtable.add(select_tag) |
125 | 127 | |
126 | | scroll = gtk.ScrolledWindow() |
127 | | scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) |
| 128 | scroll = Gtk.ScrolledWindow() |
| 129 | scroll.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) |
128 | 130 | scroll.add(self._textview) |
129 | 131 | |
130 | 132 | self.add2(scroll) |
131 | 133 | |
132 | | def _sort_logfile(self, treemodel, itera, iterb): |
| 134 | def _sort_logfile(self, treemodel, itera, iterb, user_data=None): |
133 | 135 | a = treemodel.get_value(itera, 0) |
134 | 136 | b = treemodel.get_value(iterb, 0) |
135 | 137 | if a == None or b == None: |
… |
… |
class MultiLogView(gtk.HPaned): |
162 | 164 | def _configure_watcher(self): |
163 | 165 | # Setting where GIO will be watching |
164 | 166 | for p in self.paths: |
165 | | monitor = gio.File(p).monitor_directory() |
| 167 | monitor = Gio.File.new_for_path(p)\ |
| 168 | .monitor_directory(Gio.FileMonitorFlags.NONE, None) |
166 | 169 | monitor.connect('changed', self._log_file_changed_cb) |
167 | 170 | self._gio_monitors.append(monitor) |
168 | 171 | |
169 | 172 | for f in self.extra_files: |
170 | | monitor = gio.File(f).monitor_file() |
| 173 | monitor = Gio.File.new_for_path(f)\ |
| 174 | .monitor_file(Gio.FileMonitorFlags.NONE, None) |
171 | 175 | monitor.connect('changed', self._log_file_changed_cb) |
172 | 176 | self._gio_monitors.append(monitor) |
173 | 177 | |
174 | 178 | def _log_file_changed_cb(self, monitor, log_file, other_file, event): |
175 | 179 | logfile = log_file.get_basename() |
176 | 180 | |
177 | | if event == gio.FILE_MONITOR_EVENT_CHANGED: |
| 181 | if event == Gio.FileMonitorEvent.CHANGED: |
178 | 182 | if logfile in self.logs: |
179 | 183 | self.logs[logfile].update() |
180 | | elif event == gio.FILE_MONITOR_EVENT_DELETED: |
| 184 | elif event == Gio.FileMonitorEvent.DELETED: |
181 | 185 | if logfile in self.logs: |
182 | 186 | self._remove_log_file(logfile) |
183 | | elif event == gio.FILE_MONITOR_EVENT_CREATED: |
| 187 | elif event == Gio.FileMonitorEvent.CREATED: |
184 | 188 | self._add_log_file(log_file.get_path()) |
185 | 189 | |
186 | 190 | def _cursor_changed_cb(self, treeview): |
187 | 191 | treestore, text_iter = self._treeview.get_selection().get_selected() |
188 | | self._show_log(treestore.get_value(text_iter, 0)) |
| 192 | # FIXME: None is not a possible value |
| 193 | if text_iter: |
| 194 | self._show_log(treestore.get_value(text_iter, 0)) |
189 | 195 | |
190 | 196 | def _show_log(self, logfile): |
191 | 197 | if logfile in self.logs: |
192 | 198 | log = self.logs[logfile] |
193 | 199 | self._textview.set_buffer(log) |
194 | | self._textview.scroll_to_mark(log.get_insert(), 0) |
| 200 | self._textview.scroll_to_mark( |
| 201 | log.get_insert(), 0, use_align=False, xalign=0.5, yalign=0.5) |
195 | 202 | self.active_log = log |
196 | 203 | |
197 | 204 | def _find_logs(self): |
… |
… |
class MultiLogView(gtk.HPaned): |
241 | 248 | written = log._written |
242 | 249 | |
243 | 250 | if self.active_log == None: |
| 251 | # import epdb;epdb.set_trace() |
244 | 252 | self.active_log = log |
245 | 253 | self._show_log(logfile) |
246 | | log_iter = \ |
247 | | self._treeview.get_model().convert_child_iter_to_iter(None, |
248 | | log.iter) |
| 254 | # README: I don't understand how it works, sometimes it fails |
| 255 | # http://developer.gnome.org/gtk3/3.5/GtkTreeModelSort.html#gtk-tree-model-sort-convert-child-iter-to-iter |
| 256 | success, log_iter = \ |
| 257 | self._treeview.get_model().convert_child_iter_to_iter(log.iter) |
249 | 258 | self._treeview.get_selection().select_iter(log_iter) |
250 | 259 | |
251 | 260 | if written > 0 and self.active_log == log: |
252 | | self._textview.scroll_to_mark(log.get_insert(), 0) |
| 261 | self._textview.scroll_to_mark( |
| 262 | log.get_insert(), 0, use_align=False, xalign=0.5, yalign=0.5) |
253 | 263 | |
254 | 264 | def _remove_log_file(self, logfile): |
255 | 265 | log = self.logs[logfile] |
… |
… |
class MultiLogView(gtk.HPaned): |
268 | 278 | _buffer.remove_tag_by_name('search-select', start, end) |
269 | 279 | |
270 | 280 | text_iter = _buffer.get_start_iter() |
271 | | while True: |
272 | | next_found = text_iter.forward_search(text, 0) |
273 | | if next_found is None: |
274 | | break |
275 | | start, end = next_found |
276 | | _buffer.apply_tag_by_name('search-hilite', start, end) |
277 | | text_iter = end |
| 281 | |
| 282 | # FIXME: this search is not working due to this bug |
| 283 | # https://bugzilla.gnome.org/show_bug.cgi?id=680597 |
| 284 | # while True: |
| 285 | # next_found = text_iter.forward_search(text, 0) |
| 286 | # if next_found is None: |
| 287 | # break |
| 288 | # start, end = next_found |
| 289 | # _buffer.apply_tag_by_name('search-hilite', start, end) |
| 290 | # text_iter = end |
278 | 291 | |
279 | 292 | if self.get_next_result('current'): |
280 | 293 | self.search_next('current') |
… |
… |
class MultiLogView(gtk.HPaned): |
312 | 325 | self._textview.scroll_to_iter(end, 0.1) |
313 | 326 | |
314 | 327 | |
315 | | class LogBuffer(gtk.TextBuffer): |
| 328 | class LogBuffer(Gtk.TextBuffer): |
316 | 329 | |
317 | 330 | def __init__(self, tagtable, logfile, iterator): |
318 | | gtk.TextBuffer.__init__(self, tagtable) |
| 331 | # GObject.GObject.__init__(self, tagtable) |
| 332 | GObject.GObject.__init__(self) |
| 333 | |
| 334 | # FIXME: it's not possible to set this value |
| 335 | # self.props.tag_table = tagtable |
319 | 336 | |
320 | 337 | self.logfile = logfile |
321 | 338 | self._pos = 0 |
… |
… |
class LogActivity(activity.Activity): |
367 | 384 | |
368 | 385 | self._build_toolbox() |
369 | 386 | |
| 387 | # Get Sugar's clipboard |
| 388 | self.clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD) |
370 | 389 | self.show() |
371 | 390 | |
372 | 391 | def _build_toolbox(self): |
… |
… |
class LogActivity(activity.Activity): |
389 | 408 | toolbar_box.toolbar.insert(wrap_btn, -1) |
390 | 409 | |
391 | 410 | self.search_entry = iconentry.IconEntry() |
392 | | self.search_entry.set_size_request(gtk.gdk.screen_width() / 3, -1) |
| 411 | self.search_entry.set_size_request(Gdk.Screen.width() / 3, -1) |
393 | 412 | self.search_entry.set_icon_from_name( |
394 | 413 | iconentry.ICON_ENTRY_PRIMARY, 'system-search') |
395 | 414 | self.search_entry.add_clear_button() |
396 | 415 | self.search_entry.connect('activate', self._search_entry_activate_cb) |
397 | 416 | self.search_entry.connect('changed', self._search_entry_changed_cb) |
398 | | search_item = gtk.ToolItem() |
| 417 | search_item = Gtk.ToolItem() |
399 | 418 | search_item.add(self.search_entry) |
400 | 419 | toolbar_box.toolbar.insert(search_item, -1) |
401 | 420 | |
… |
… |
class LogActivity(activity.Activity): |
423 | 442 | delete_btn.connect('clicked', self._delete_log_cb) |
424 | 443 | toolbar_box.toolbar.insert(delete_btn, -1) |
425 | 444 | |
426 | | separator = gtk.SeparatorToolItem() |
| 445 | separator = Gtk.SeparatorToolItem() |
427 | 446 | separator.set_expand(True) |
428 | 447 | separator.set_draw(False) |
429 | 448 | toolbar_box.toolbar.insert(separator, -1) |
… |
… |
class LogActivity(activity.Activity): |
435 | 454 | |
436 | 455 | def __copy_clicked_cb(self, button): |
437 | 456 | if self.viewer.active_log: |
438 | | self.viewer.active_log.copy_clipboard(gtk.clipboard_get()) |
| 457 | self.viewer.active_log.copy_clipboard(self.clipboard) |
439 | 458 | |
440 | 459 | def _wrap_cb(self, button): |
441 | 460 | if button.get_active(): |
442 | | self.viewer._textview.set_wrap_mode(gtk.WRAP_WORD_CHAR) |
| 461 | self.viewer._textview.set_wrap_mode(Gtk.WrapMode.WORD_CHAR) |
443 | 462 | else: |
444 | | self.viewer._textview.set_wrap_mode(gtk.WRAP_NONE) |
| 463 | self.viewer._textview.set_wrap_mode(Gtk.WrapMode.NONE) |
445 | 464 | |
446 | 465 | def _search_entry_activate_cb(self, entry): |
447 | 466 | if self._autosearch_timer: |
448 | | gobject.source_remove(self._autosearch_timer) |
| 467 | GObject.source_remove(self._autosearch_timer) |
449 | 468 | self.viewer.set_search_text(entry.props.text) |
450 | 469 | self._update_search_buttons() |
451 | 470 | |
452 | 471 | def _search_entry_changed_cb(self, entry): |
453 | 472 | if self._autosearch_timer: |
454 | | gobject.source_remove(self._autosearch_timer) |
455 | | self._autosearch_timer = gobject.timeout_add(_AUTOSEARCH_TIMEOUT, |
| 473 | GObject.source_remove(self._autosearch_timer) |
| 474 | self._autosearch_timer = GObject.timeout_add(_AUTOSEARCH_TIMEOUT, |
456 | 475 | self.__autosearch_timer_cb) |
457 | 476 | |
458 | 477 | def __autosearch_timer_cb(self): |
… |
… |
class CollectorPalette(Palette): |
503 | 522 | |
504 | 523 | self._collector = LogCollect() |
505 | 524 | |
506 | | label = gtk.Label( |
| 525 | label = Gtk.Label(label= |
507 | 526 | _('This captures information about the system\n'\ |
508 | 527 | 'and running processes to a journal entry.\n'\ |
509 | 528 | 'Use this to improve a problem report.')) |
510 | 529 | |
511 | | send_button = gtk.Button(_('Capture information')) |
| 530 | send_button = Gtk.Button(_('Capture information')) |
512 | 531 | send_button.connect('clicked', self._on_send_button_clicked_cb) |
513 | 532 | |
514 | | vbox = gtk.VBox(False, 5) |
515 | | vbox.pack_start(label) |
516 | | vbox.pack_start(send_button) |
| 533 | vbox = Gtk.VBox(False, 5) |
| 534 | vbox.pack_start(label, True, True, 0) |
| 535 | vbox.pack_start(send_button, True, True, 0) |
517 | 536 | vbox.show_all() |
518 | 537 | |
519 | 538 | self.set_content(vbox) |
diff --git a/setup.py b/setup.py
index 876cd3f..95390a3 100755
a
|
b
|
|
16 | 16 | # along with this program; if not, write to the Free Software |
17 | 17 | # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
18 | 18 | |
19 | | from sugar.activity import bundlebuilder |
| 19 | from sugar3.activity import bundlebuilder |
20 | 20 | |
21 | 21 | bundlebuilder.start() |
22 | 22 | |