fpdb/pyfpdb/GuiLogView.py

223 lines
8.4 KiB
Python
Raw Normal View History

#!/usr/bin/env python
2009-12-05 16:54:49 +01:00
# -*- coding: utf-8 -*-
#Copyright 2008-2010 Carl Gherardi
2009-12-05 16:54:49 +01:00
#This program is free software: you can redistribute it and/or modify
#it under the terms of the GNU Affero General Public License as published by
#the Free Software Foundation, version 3 of the License.
#
#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 Affero General Public License
#along with this program. If not, see <http://www.gnu.org/licenses/>.
#In the "official" distribution you can find the license in agpl-3.0.txt.
2009-12-05 16:54:49 +01:00
2010-09-23 07:11:06 +02:00
import L10n
_ = L10n.get_translation()
import os
import Queue
2009-12-05 16:54:49 +01:00
import pygtk
pygtk.require('2.0')
import gtk
import gobject
import pango
2010-07-17 19:19:16 +02:00
import os
import traceback
import logging
# logging has been set up in fpdb.py or HUD_main.py, use their settings:
log = logging.getLogger("logview")
2009-12-05 16:54:49 +01:00
MAX_LINES = 100000 # max lines to display in window
EST_CHARS_PER_LINE = 150 # used to guesstimate number of lines in log file
LOGFILES = [ [ 'Fpdb Errors', 'fpdb-errors.txt', False ] # label, filename, start value
, [ 'Fpdb Log', 'fpdb-log.txt', True ]
, [ 'HUD Errors', 'HUD-errors.txt', False ]
, [ 'HUD Log', 'HUD-log.txt', False ]
]
2009-12-05 21:10:00 +01:00
2009-12-05 16:54:49 +01:00
class GuiLogView:
def __init__(self, config, mainwin, closeq):
2009-12-05 16:54:49 +01:00
self.config = config
self.main_window = mainwin
self.closeq = closeq
2009-12-07 23:55:12 +01:00
self.logfile = os.path.join(self.config.dir_log, LOGFILES[1][1])
2010-08-15 18:43:30 +02:00
self.dia = gtk.Dialog(title=_("Log Messages")
2009-12-07 23:55:12 +01:00
,parent=None
,flags=gtk.DIALOG_DESTROY_WITH_PARENT
,buttons=(gtk.STOCK_CLOSE,gtk.RESPONSE_OK))
self.dia.set_modal(False)
self.vbox = self.dia.vbox
2009-12-05 16:54:49 +01:00
gtk.Widget.set_size_request(self.vbox, 700, 400);
2009-12-05 21:10:00 +01:00
self.liststore = gtk.ListStore(str, str, str, str, gobject.TYPE_BOOLEAN) # date, module, level, text
# this is how to add a filter:
#
# # Creation of the filter, from the model
# filter = self.liststore.filter_new()
# filter.set_visible_column(1)
#
# # The TreeView gets the filter as model
# self.listview = gtk.TreeView(filter)
2009-12-05 16:54:49 +01:00
self.listview = gtk.TreeView(model=self.liststore)
2009-12-05 21:10:00 +01:00
self.listview.set_grid_lines(gtk.TREE_VIEW_GRID_LINES_NONE)
2009-12-05 23:48:41 +01:00
self.listcols = []
2009-12-05 16:54:49 +01:00
scrolledwindow = gtk.ScrolledWindow()
scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
scrolledwindow.add(self.listview)
2009-12-05 23:48:41 +01:00
self.vbox.pack_start(scrolledwindow, expand=True, fill=True, padding=0)
hb = gtk.HBox(False, 0)
grp = None
for logf in LOGFILES:
rb = gtk.RadioButton(group=grp, label=logf[0], use_underline=True)
if grp is None: grp = rb
rb.set_active(logf[2])
rb.connect('clicked', self.__set_logfile, logf[0])
hb.pack_start(rb, False, False, 3)
2010-08-15 18:43:30 +02:00
refreshbutton = gtk.Button(_("Refresh"))
2009-12-05 23:48:41 +01:00
refreshbutton.connect("clicked", self.refresh, None)
hb.pack_start(refreshbutton, False, False, 3)
2009-12-05 23:48:41 +01:00
refreshbutton.show()
self.vbox.pack_start(hb, False, False, 0)
2009-12-05 16:54:49 +01:00
self.listview.show()
scrolledwindow.show()
self.vbox.show()
2009-12-07 23:55:12 +01:00
self.dia.set_focus(self.listview)
2009-12-05 16:54:49 +01:00
2009-12-05 23:48:41 +01:00
col = self.addColumn("Date/Time", 0)
col = self.addColumn("Module", 1)
col = self.addColumn("Level", 2)
col = self.addColumn("Text", 3)
2009-12-05 16:54:49 +01:00
self.loadLog()
self.vbox.show_all()
2009-12-07 23:55:12 +01:00
self.dia.show()
self.dia.connect('response', self.dialog_response_cb)
def __set_logfile(self, w, file):
#print "w is", w, "file is", file, "active is", w.get_active()
if w.get_active():
for logf in LOGFILES:
if logf[0] == file:
self.logfile = os.path.join(self.config.dir_log, logf[1])
self.refresh(w, file) # params are not used
2009-12-07 23:55:12 +01:00
def dialog_response_cb(self, dialog, response_id):
# this is called whether close button is pressed or window is closed
self.closeq.put(self.__class__)
2009-12-07 23:55:12 +01:00
dialog.destroy()
def get_dialog(self):
return self.dia
2009-12-05 16:54:49 +01:00
2009-12-05 23:48:41 +01:00
def addColumn(self, title, n):
col = gtk.TreeViewColumn(title)
self.listview.append_column(col)
cRender = gtk.CellRendererText()
cRender.set_property("wrap-mode", pango.WRAP_WORD_CHAR)
col.pack_start(cRender, True)
col.add_attribute(cRender, 'text', n)
col.set_max_width(1000)
col.set_spacing(0) # no effect
self.listcols.append(col)
col.set_clickable(True)
col.connect("clicked", self.sortCols, n)
return(col)
2009-12-05 16:54:49 +01:00
def loadLog(self):
self.liststore.clear()
2010-07-17 19:19:16 +02:00
# self.listcols = [] blanking listcols causes sortcols() to fail with index out of range
2009-12-05 16:54:49 +01:00
# guesstimate number of lines in file
if os.path.exists(self.logfile):
stat_info = os.stat(self.logfile)
lines = stat_info.st_size / EST_CHARS_PER_LINE
#print "logview: size =", stat_info.st_size, "lines =", lines
# set startline to line number to start display from
startline = 0
if lines > MAX_LINES:
# only display from startline if log file is large
startline = lines - MAX_LINES
l = 0
for line in open(self.logfile):
# example line in logfile format:
# 2009-12-02 15:23:21,716 - config DEBUG config logger initialised
l = l + 1
if l > startline:
2010-07-17 19:19:16 +02:00
# NOTE selecting a sort column and then switching to a log file
# with several thousand rows will send cpu 100% for a prolonged period.
# reason is that the append() method seems to sort every record as it goes, rather than
# pulling in the whole file and sorting at the end.
# one fix is to check if a column sort has been selected, reset to date/time asc
# append all the rows and then reselect the required sort order.
# Note: there is no easy method available to revert the list to an "unsorted" state.
# always defaulting to date/time asc doesn't work, because some rows do not have date/time info
# and would end up sorted out of context.
if len(line) > 49 and line[23:26] == ' - ' and line[34:39] == ' ':
iter = self.liststore.append( (line[0:23], line[26:32], line[39:46], line[48:].strip(), True) )
else:
iter = self.liststore.append( ('', '', '', line.strip(), True) )
2009-12-05 16:54:49 +01:00
2009-12-05 21:10:00 +01:00
def sortCols(self, col, n):
try:
if not col.get_sort_indicator() or col.get_sort_order() == gtk.SORT_ASCENDING:
col.set_sort_order(gtk.SORT_DESCENDING)
else:
col.set_sort_order(gtk.SORT_ASCENDING)
self.liststore.set_sort_column_id(n, col.get_sort_order())
#self.liststore.set_sort_func(n, self.sortnums, (n,grid))
for i in xrange(len(self.listcols)):
self.listcols[i].set_sort_indicator(False)
self.listcols[n].set_sort_indicator(True)
# use this listcols[col].set_sort_indicator(True)
# to turn indicator off for other cols
except:
err = traceback.extract_tb(sys.exc_info()[2])
2010-08-15 18:43:30 +02:00
print _("***sortCols error: ") + str(sys.exc_info()[1])
2009-12-05 21:10:00 +01:00
print "\n".join( [e[0]+':'+str(e[1])+" "+e[2] for e in err] )
2009-12-05 16:54:49 +01:00
2009-12-05 23:48:41 +01:00
def refresh(self, widget, data):
self.loadLog()
2009-12-05 16:54:49 +01:00
if __name__=="__main__":
config = Configuration.Config()
win = gtk.Window(gtk.WINDOW_TOPLEVEL)
2010-08-15 18:43:30 +02:00
win.set_title(_("Test Log Viewer"))
2009-12-05 16:54:49 +01:00
win.set_border_width(1)
win.set_default_size(600, 500)
win.set_resizable(True)
2010-08-15 18:43:30 +02:00
dia = gtk.Dialog(_("Log Viewer"),
2009-12-05 16:54:49 +01:00
win,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
(gtk.STOCK_CLOSE, gtk.RESPONSE_OK))
dia.set_default_size(500, 500)
log = GuiLogView(config, win, dia.vbox)
response = dia.run()
if response == gtk.RESPONSE_ACCEPT:
pass
dia.destroy()