Use debian 2.7 only
[fpbd-bostik.git] / pyfpdb / GuiLogView.py
blob97fdcae9a8a311f42c689a807536208d10f1d8b5
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
4 #Copyright 2008-2011 Carl Gherardi
5 #This program is free software: you can redistribute it and/or modify
6 #it under the terms of the GNU Affero General Public License as published by
7 #the Free Software Foundation, version 3 of the License.
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.
14 #You should have received a copy of the GNU Affero General Public License
15 #along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #In the "official" distribution you can find the license in agpl-3.0.txt.
18 import L10n
19 _ = L10n.get_translation()
21 import os
22 import Queue
24 import pygtk
25 pygtk.require('2.0')
26 import gtk
27 import gobject
28 import pango
30 import os
31 import traceback
32 import logging
33 import Configuration
34 if __name__ == "__main__":
35 Configuration.set_logfile("fpdb-log.txt")
36 # logging has been set up in fpdb.py or HUD_main.py, use their settings:
37 log = logging.getLogger("logview")
39 MAX_LINES = 100000 # max lines to display in window
40 EST_CHARS_PER_LINE = 150 # used to guesstimate number of lines in log file
41 LOGFILES = [ [ _('Fpdb Errors'), 'fpdb-errors.txt', False ] # label, filename, start value
42 , [ _('Fpdb Log'), 'fpdb-log.txt', True ]
43 , [ _('HUD Errors'), 'HUD-errors.txt', False ]
44 , [ _('HUD Log'), 'HUD-log.txt', False ]
47 class GuiLogView:
49 def __init__(self, config, mainwin, closeq):
50 self.config = config
51 self.main_window = mainwin
52 self.closeq = closeq
54 self.logfile = os.path.join(self.config.dir_log, LOGFILES[1][1])
55 self.dia = gtk.Dialog(title=_("Log Messages")
56 ,parent=None
57 ,flags=gtk.DIALOG_DESTROY_WITH_PARENT
58 ,buttons=(gtk.STOCK_CLOSE,gtk.RESPONSE_OK))
59 self.dia.set_modal(False)
61 self.vbox = self.dia.vbox
62 gtk.Widget.set_size_request(self.vbox, 700, 400);
64 self.liststore = gtk.ListStore(str, str, str, str, gobject.TYPE_BOOLEAN) # date, module, level, text
65 # this is how to add a filter:
67 # # Creation of the filter, from the model
68 # filter = self.liststore.filter_new()
69 # filter.set_visible_column(1)
71 # # The TreeView gets the filter as model
72 # self.listview = gtk.TreeView(filter)
73 self.listview = gtk.TreeView(model=self.liststore)
74 self.listview.set_grid_lines(gtk.TREE_VIEW_GRID_LINES_NONE)
75 self.listcols = []
77 scrolledwindow = gtk.ScrolledWindow()
78 scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
79 scrolledwindow.add(self.listview)
80 self.vbox.pack_start(scrolledwindow, expand=True, fill=True, padding=0)
82 hb = gtk.HBox(False, 0)
83 grp = None
84 for logf in LOGFILES:
85 rb = gtk.RadioButton(group=grp, label=logf[0], use_underline=True)
86 if grp is None: grp = rb
87 rb.set_active(logf[2])
88 rb.connect('clicked', self.__set_logfile, logf[0])
89 hb.pack_start(rb, False, False, 3)
90 refreshbutton = gtk.Button(_("Refresh"))
91 refreshbutton.connect("clicked", self.refresh, None)
92 hb.pack_start(refreshbutton, False, False, 3)
93 refreshbutton.show()
94 self.vbox.pack_start(hb, False, False, 0)
96 self.listview.show()
97 scrolledwindow.show()
98 self.vbox.show()
99 self.dia.set_focus(self.listview)
101 col = self.addColumn(_("Date/Time"), 0)
102 col = self.addColumn(_("Module"), 1)
103 col = self.addColumn(_("Level"), 2)
104 col = self.addColumn(_("Text"), 3)
106 self.loadLog()
107 self.vbox.show_all()
108 self.dia.show()
110 self.dia.connect('response', self.dialog_response_cb)
112 def __set_logfile(self, w, file):
113 #print "w is", w, "file is", file, "active is", w.get_active()
114 if w.get_active():
115 for logf in LOGFILES:
116 if logf[0] == file:
117 self.logfile = os.path.join(self.config.dir_log, logf[1])
118 self.refresh(w, file) # params are not used
120 def dialog_response_cb(self, dialog, response_id):
121 # this is called whether close button is pressed or window is closed
122 self.closeq.put(self.__class__)
123 dialog.destroy()
125 def get_dialog(self):
126 return self.dia
128 def addColumn(self, title, n):
129 col = gtk.TreeViewColumn(title)
130 self.listview.append_column(col)
131 cRender = gtk.CellRendererText()
132 cRender.set_property("wrap-mode", pango.WRAP_WORD_CHAR)
133 col.pack_start(cRender, True)
134 col.add_attribute(cRender, 'text', n)
135 col.set_max_width(1000)
136 col.set_spacing(0) # no effect
137 self.listcols.append(col)
138 col.set_clickable(True)
139 col.connect("clicked", self.sortCols, n)
140 return(col)
142 def loadLog(self):
144 self.liststore.clear()
145 # self.listcols = [] blanking listcols causes sortcols() to fail with index out of range
147 # guesstimate number of lines in file
148 if os.path.exists(self.logfile):
149 stat_info = os.stat(self.logfile)
150 lines = stat_info.st_size / EST_CHARS_PER_LINE
151 #print "logview: size =", stat_info.st_size, "lines =", lines
153 # set startline to line number to start display from
154 startline = 0
155 if lines > MAX_LINES:
156 # only display from startline if log file is large
157 startline = lines - MAX_LINES
159 l = 0
160 for line in open(self.logfile):
161 # example line in logfile format:
162 # 2009-12-02 15:23:21,716 - config DEBUG config logger initialised
163 l = l + 1
164 if l > startline:
165 # NOTE selecting a sort column and then switching to a log file
166 # with several thousand rows will send cpu 100% for a prolonged period.
167 # reason is that the append() method seems to sort every record as it goes, rather than
168 # pulling in the whole file and sorting at the end.
169 # one fix is to check if a column sort has been selected, reset to date/time asc
170 # append all the rows and then reselect the required sort order.
171 # Note: there is no easy method available to revert the list to an "unsorted" state.
172 # always defaulting to date/time asc doesn't work, because some rows do not have date/time info
173 # and would end up sorted out of context.
174 if len(line) > 49 and line[23:26] == ' - ' and line[34:39] == ' ':
175 iter = self.liststore.append( (line[0:23], line[26:32], line[39:46], line[48:].strip(), True) )
176 else:
177 iter = self.liststore.append( ('', '', '', line.strip(), True) )
179 def sortCols(self, col, n):
180 if not col.get_sort_indicator() or col.get_sort_order() == gtk.SORT_ASCENDING:
181 col.set_sort_order(gtk.SORT_DESCENDING)
182 else:
183 col.set_sort_order(gtk.SORT_ASCENDING)
184 self.liststore.set_sort_column_id(n, col.get_sort_order())
185 #self.liststore.set_sort_func(n, self.sortnums, (n,grid))
186 for i in xrange(len(self.listcols)):
187 self.listcols[i].set_sort_indicator(False)
188 self.listcols[n].set_sort_indicator(True)
189 # use this listcols[col].set_sort_indicator(True)
190 # to turn indicator off for other cols
192 def refresh(self, widget, data):
193 self.loadLog()
197 if __name__=="__main__":
199 config = Configuration.Config()
201 win = gtk.Window(gtk.WINDOW_TOPLEVEL)
202 win.set_title(_("Log Viewer"))
203 win.set_border_width(1)
204 win.set_default_size(600, 500)
205 win.set_resizable(True)
207 dia = gtk.Dialog(_("Log Viewer"),
208 win,
209 gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
210 (gtk.STOCK_CLOSE, gtk.RESPONSE_OK))
211 dia.set_default_size(500, 500)
212 log = GuiLogView(config, win, dia.vbox)
213 response = dia.run()
214 if response == gtk.RESPONSE_ACCEPT:
215 pass
216 dia.destroy()