#!/usr/bin/env python # Copyright (C) 2006-2007, Eduardo Silva # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import os import logging from gettext import gettext as _ import gtk import dbus import pygtk import gobject import pango import gnomevfs from sugar.activity import activity from sugar import env from sugar.graphics.toolbutton import ToolButton from sugar.graphics.palette import Palette from logcollect import LogCollect, LogSend class MultiLogView(gtk.VBox): def __init__(self, path, extra_files): self._logs_path = path self._active_log = None self._extra_files = extra_files # Creating Main treeview with Actitivities list self._tv_menu = gtk.TreeView() self._tv_menu.connect('cursor-changed', self._load_log) self._tv_menu.set_rules_hint(True) # Set width box_width = gtk.gdk.screen_width() * 80 / 100 self._tv_menu.set_size_request(box_width*25/100, 0) self._store_menu = gtk.TreeStore(str) self._tv_menu.set_model(self._store_menu) self._add_column(self._tv_menu, 'Sugar logs', 0) self._logs = {} # Activities menu self.hbox = gtk.HBox(False, 3) self.hbox.pack_start(self._tv_menu, True, True, 0) # Activity log, set width self._view = LogView() self._view.set_size_request(box_width*75/100, 0) self.hbox.pack_start(self._view, True, True, 0) self.hbox.show_all() self._configure_watcher() self._create_log_view() def _configure_watcher(self): # Setting where gnomeVFS will be watching gnomevfs.monitor_add('file://' + self._logs_path, gnomevfs.MONITOR_DIRECTORY, self._log_file_changed_cb) for f in self._extra_files: gnomevfs.monitor_add('file://' + f, gnomevfs.MONITOR_FILE, self._log_file_changed_cb) def _log_file_changed_cb(self, monitor_uri, info_uri, event): path = info_uri.split('file://')[-1] filename = self._get_filename_from_path(path) if event == gnomevfs.MONITOR_EVENT_CHANGED: self._logs[filename].update() elif event == gnomevfs.MONITOR_EVENT_DELETED: self._delete_log_file_view(filename) elif event == gnomevfs.MONITOR_EVENT_CREATED: self._add_log_file(path) # Load the log information in View (textview) def _load_log(self, treeview): treeselection = treeview.get_selection() treestore, iter = treeselection.get_selected() # Get current selection act_log = self._store_menu.get_value(iter, 0) # Set buffer and scroll down self._view.textview.set_buffer(self._logs[act_log]) self._view.textview.scroll_to_mark(self._logs[act_log].get_insert(), 0) self._active_log = act_log def _create_log_view(self): # Searching log files for logfile in os.listdir(self._logs_path): full_log_path = os.path.join(self._logs_path, logfile) self._add_log_file(full_log_path) for ext in self._extra_files: self._add_log_file(ext) return True def _delete_log_file_view(self, logkey): self._store_menu.remove(self._logs[logkey].iter) del self._logs[logkey] def _get_filename_from_path(self, path): return path.split('/')[-1] def _add_log_file(self, path): if os.path.isdir(path): return False if not os.path.exists(path): print "ERROR: %s don't exists" % path return False if not os.access(path, os.R_OK): print "ERROR: I can't read '%s' file" % path return False logfile = self._get_filename_from_path(path) if not self._logs.has_key(logfile): iter = self._add_log_row(logfile) model = LogBuffer(path, iter) self._logs[logfile] = model self._logs[logfile].update() written = self._logs[logfile]._written # Load the first iter if self._active_log == None: self._active_log = logfile iter = self._tv_menu.get_model().get_iter_root() self._tv_menu.get_selection().select_iter(iter) self._load_log(self._tv_menu) if written > 0 and self._active_log == logfile: self._view.textview.scroll_to_mark(self._logs[logfile].get_insert(), 0) def _add_log_row(self, name): return self._insert_row(self._store_menu, None, name) # Add a new column to the main treeview, (code from Memphis) def _add_column(self, treeview, column_name, index): cell = gtk.CellRendererText() col_tv = gtk.TreeViewColumn(column_name, cell, text=index) col_tv.set_resizable(True) col_tv.set_property('clickable', True) treeview.append_column(col_tv) # Set the last column index added self.last_col_index = index # Insert a Row in our TreeView def _insert_row(self, store, parent, name): iter = store.insert_before(parent, None) index = 0 store.set_value(iter, index , name) return iter class LogBuffer(gtk.TextBuffer): def __init__(self, logfile, iter=None): gtk.TextBuffer.__init__(self) self._logfile = logfile self._pos = 0 self.iter = iter self.update() def update(self): try: f = open(self._logfile, 'r') init_pos = self._pos f.seek(self._pos) self.insert(self.get_end_iter(), f.read()) self._pos = f.tell() f.close() self._written = (self._pos - init_pos) except: self.insert(self.get_end_iter(), "Console error: can't open the file\n") self._written = 0 class LogView(gtk.ScrolledWindow): def __init__(self): gtk.ScrolledWindow.__init__(self) self.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) self.textview = gtk.TextView() self.textview.set_wrap_mode(gtk.WRAP_WORD) # Set background color bgcolor = gtk.gdk.color_parse("#FFFFFF") self.textview.modify_base(gtk.STATE_NORMAL, bgcolor) self.textview.set_editable(False) self.add(self.textview) self.textview.show() class LogHandler(activity.Activity): def __init__(self, handle): activity.Activity.__init__(self, handle) logging.debug('Starting the Log Viewer activity') self.set_title(_('Log Viewer Activity')) # Main path to watch: ~/.sugar/someuser/logs... main_path = os.path.join(env.get_profile_path(), 'logs') # extra files to watch in logviewer ext_files = [] ext_files.append("/var/log/Xorg.0.log") ext_files.append("/var/log/syslog") ext_files.append("/var/log/messages") self._viewer = MultiLogView(main_path, ext_files).hbox self._box = gtk.HBox() self._box.pack_start(self._viewer) self._box.show() self.set_canvas(self._box) # TOOLBAR toolbox = activity.ActivityToolbox(self) toolbox.show() toolbar = LogToolbar(self) toolbox.add_toolbar(_('Tools'), toolbar) toolbar.show() self.set_toolbox(toolbox) self.show() # Dirty hide() toolbar = toolbox.get_activity_toolbar() toolbar.share.hide() toolbar.keep.hide() # Keeping this method to add new funcs later def switch_to_logviewer(self): self._clean_box() self._box.pack_start(self._viewer) class LogToolbar(gtk.Toolbar): def __init__(self, handler): gtk.Toolbar.__init__(self) self._handler = handler collector_palette = CollectorMenu() logviewer = ToolButton('zoom-best-fit') logviewer.set_palette(collector_palette) logviewer.connect('clicked', self._on_logviewer_clicked_cb) self.insert(logviewer, -1) logviewer.show() def _on_logviewer_clicked_cb(self, widget): self._handler.switch_to_logviewer() class CollectorMenu(Palette): _DEFAULT_SERVER = 'http://pascal.scheffers.net/olpc/submit.tcl' def __init__(self): Palette.__init__(self, 'Log Collector: send XO information') self._collector = LogCollect() label = gtk.Label(_('Log collector allow to send information about\n\ the system and running process to a central\nserver, use this option if you \ want to report\nsome detected problem')) send_button = gtk.Button(_('Send information')) send_button.connect('clicked', self._on_send_button_clicked_cb) vbox = gtk.VBox(False, 5) vbox.pack_start(label) vbox.pack_start(send_button) vbox.show_all() self.set_content(vbox) def _on_send_button_clicked_cb(self, button): # Using the default values, just for testing... data = self._collector.write_logs() sender = LogSend() if sender.http_post_logs(self._DEFAULT_SERVER, data): print "Logs sent...OK" else: print "FAILED to send logs" os.remove(data) self.popdown()