diff --git a/pyfpdb/GuiLogView.py b/pyfpdb/GuiLogView.py index 3b7b8aa6..29385fa0 100755 --- a/pyfpdb/GuiLogView.py +++ b/pyfpdb/GuiLogView.py @@ -17,7 +17,8 @@ #agpl-3.0.txt in the docs folder of the package. -import mmap +import os +import Queue import pygtk pygtk.require('2.0') @@ -29,18 +30,27 @@ import Configuration log = Configuration.get_logger("logging.conf", "logview") -MAX_LINES = 100000 +MAX_LINES = 100000 # max lines to display in window +EST_CHARS_PER_LINE = 150 # used to guesstimate number of lines in log file +logfile = 'logging.out' # name of logfile class GuiLogView: - def __init__(self, config, mainwin, vbox): + def __init__(self, config, mainwin, closeq): self.config = config self.main_window = mainwin - self.vbox = vbox + self.closeq = closeq + + self.dia = gtk.Dialog(title="Log Messages" + ,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 gtk.Widget.set_size_request(self.vbox, 700, 400); 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 @@ -49,7 +59,6 @@ class GuiLogView: # # # The TreeView gets the filter as model # self.listview = gtk.TreeView(filter) - self.listview = gtk.TreeView(model=self.liststore) self.listview.set_grid_lines(gtk.TREE_VIEW_GRID_LINES_NONE) self.listcols = [] @@ -67,6 +76,7 @@ class GuiLogView: self.listview.show() scrolledwindow.show() self.vbox.show() + self.dia.set_focus(self.listview) col = self.addColumn("Date/Time", 0) col = self.addColumn("Module", 1) @@ -75,6 +85,17 @@ class GuiLogView: self.loadLog() self.vbox.show_all() + self.dia.show() + + self.dia.connect('response', self.dialog_response_cb) + + 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__) + dialog.destroy() + + def get_dialog(self): + return self.dia def addColumn(self, title, n): col = gtk.TreeViewColumn(title) @@ -92,39 +113,28 @@ class GuiLogView: def loadLog(self): - #self.configStore = gtk.TreeStore(gobject.TYPE_PYOBJECT, gobject.TYPE_STRING, gobject.TYPE_STRING) - #self.configView = gtk.TreeView(self.configStore) - #self.configView.set_enable_tree_lines(True) self.liststore.clear() self.listcols = [] - # count number of lines in file - f = open('logging.out', "r+") - buf = mmap.mmap(f.fileno(), 0) - readline = buf.readline - lines = 0 - while readline(): - lines += 1 - f.close() + # guesstimate number of lines in file + if os.path.exists(logfile): + stat_info = os.stat(logfile) + lines = stat_info.st_size / EST_CHARS_PER_LINE + print "logview: size =", stat_info.st_size, "lines =", lines - startline = 0 - if lines > MAX_LINES: - # only display from startline if log file is large - startline = lines - MAX_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 - f = open('logging.out', "r+") - buf = mmap.mmap(f.fileno(), 0) - readline = buf.readline - l = 0 - line = readline() - while line: - # eg line: - # 2009-12-02 15:23:21,716 - config DEBUG config logger initialised - l = l + 1 - if l > startline and len(line) > 49: - iter = self.liststore.append( (line[0:23], line[26:32], line[39:46], line[48:].strip(), True) ) - line = readline() - f.close() + l = 0 + for line in open(logfile): + # eg line: + # 2009-12-02 15:23:21,716 - config DEBUG config logger initialised + l = l + 1 + if l > startline and len(line) > 49: + iter = self.liststore.append( (line[0:23], line[26:32], line[39:46], line[48:].strip(), True) ) def sortCols(self, col, n): try: diff --git a/pyfpdb/fpdb.py b/pyfpdb/fpdb.py index bfea2132..b7013a92 100755 --- a/pyfpdb/fpdb.py +++ b/pyfpdb/fpdb.py @@ -18,6 +18,7 @@ import os import sys import re +import Queue # if path is set to use an old version of python look for a new one: # (does this work in linux?) @@ -73,6 +74,7 @@ try: import pygtk pygtk.require('2.0') import gtk + import pango except: print "Unable to load PYGTK modules required for GUI. Please install PyCairo, PyGObject, and PyGTK from www.pygtk.org." raw_input("Press ENTER to continue.") @@ -80,6 +82,24 @@ except: import interlocks +# these imports not required in this module, imported here to report version in About dialog +try: + import matplotlib + matplotlib_version = matplotlib.__version__ +except: + matplotlib_version = 'not found' +try: + import numpy + numpy_version = numpy.__version__ +except: + numpy_version = 'not found' +try: + import sqlite3 + sqlite3_version = sqlite3.version + sqlite_version = sqlite3.sqlite_version +except: + sqlite3_version = 'not found' + sqlite_version = 'not found' import GuiPrefs import GuiLogView @@ -112,12 +132,12 @@ class fpdb: def add_tab(self, new_page, new_tab_name): """adds a tab, namely creates the button and displays it and appends all the relevant arrays""" - for name in self.nb_tabs: #todo: check this is valid + for name in self.nb_tab_names: #todo: check this is valid if name == new_tab_name: return # if tab already exists, just go to it used_before = False - for i, name in enumerate(self.tab_names): #todo: check this is valid + for i, name in enumerate(self.tab_names): if name == new_tab_name: used_before = True event_box = self.tabs[i] @@ -133,13 +153,13 @@ class fpdb: #self.nb.append_page(new_page, gtk.Label(new_tab_name)) self.nb.append_page(page, event_box) - self.nb_tabs.append(new_tab_name) + self.nb_tab_names.append(new_tab_name) page.show() def display_tab(self, new_tab_name): """displays the indicated tab""" tab_no = -1 - for i, name in enumerate(self.nb_tabs): + for i, name in enumerate(self.nb_tab_names): if new_tab_name == name: tab_no = i break @@ -190,13 +210,13 @@ class fpdb: (nb, text) = data page = -1 #print "\n remove_tab: start", text - for i, tab in enumerate(self.nb_tabs): + for i, tab in enumerate(self.nb_tab_names): if text == tab: page = i #print " page =", page if page >= 0 and page < self.nb.get_n_pages(): #print " removing page", page - del self.nb_tabs[page] + del self.nb_tab_names[page] nb.remove_page(page) # Need to refresh the widget -- # This forces the widget to redraw itself. @@ -219,8 +239,39 @@ class fpdb: dia.set_website("http://fpdb.sourceforge.net/") dia.set_authors("Steffen, Eratosthenes, s0rrow, EricBlade, _mt, sqlcoder, Bostik, and others") dia.set_program_name("Free Poker Database (FPDB)") + + db_version = "" + #if self.db is not None: + # db_version = self.db.get_version() + nums = [ ('Operating System', os.name) + , ('Python', sys.version[0:3]) + , ('GTK+', '.'.join([str(x) for x in gtk.gtk_version])) + , ('PyGTK', '.'.join([str(x) for x in gtk.pygtk_version])) + , ('matplotlib', matplotlib_version) + , ('numpy', numpy_version) + , ('sqlite3', sqlite3_version) + , ('sqlite', sqlite_version) + , ('database', self.settings['db-server'] + db_version) + ] + versions = gtk.TextBuffer() + w = 20 # width used for module names and version numbers + versions.set_text( '\n'.join( [x[0].rjust(w)+' '+ x[1].ljust(w) for x in nums] ) ) + view = gtk.TextView(versions) + view.set_editable(False) + view.set_justification(gtk.JUSTIFY_CENTER) + view.modify_font(pango.FontDescription('monospace 10')) + view.show() + dia.vbox.pack_end(view, True, True, 2) + l = gtk.Label('Version Information:') + l.set_alignment(0.5, 0.5) + l.show() + dia.vbox.pack_end(l, True, True, 2) + dia.run() dia.destroy() + log.debug("Threads: ") + for t in self.threads: + log.debug("........." + str(t.__class__)) def dia_preferences(self, widget, data=None): dia = gtk.Dialog("Preferences", @@ -429,28 +480,52 @@ class fpdb: self.release_global_lock() def dia_logs(self, widget, data=None): - lock_set = False - if self.obtain_global_lock(): - lock_set = True + """opens the log viewer window""" - dia = gtk.Dialog(title="Log Messages" - ,parent=None - ,flags=0 - ,buttons=(gtk.STOCK_CLOSE,gtk.RESPONSE_OK)) - logviewer = GuiLogView.GuiLogView(self.config, self.window, dia.vbox) - response = dia.run() - if response == gtk.RESPONSE_ACCEPT: - pass - dia.destroy() + #lock_set = False + #if self.obtain_global_lock(): + # lock_set = True - if lock_set: - self.release_global_lock() + # remove members from self.threads if close messages received + self.process_close_messages() + + viewer = None + for i, t in enumerate(self.threads): + if str(t.__class__) == 'GuiLogView.GuiLogView': + viewer = t + break + + if viewer is None: + #print "creating new log viewer" + new_thread = GuiLogView.GuiLogView(self.config, self.window, self.closeq) + self.threads.append(new_thread) + else: + #print "showing existing log viewer" + viewer.get_dialog().present() + + #if lock_set: + # self.release_global_lock() def addLogText(self, text): end_iter = self.logbuffer.get_end_iter() self.logbuffer.insert(end_iter, text) self.logview.scroll_to_mark(self.logbuffer.get_insert(), 0) + + def process_close_messages(self): + # check for close messages + try: + while True: + name = self.closeq.get(False) + for i, t in enumerate(self.threads): + if str(t.__class__) == str(name): + # thread has ended so remove from list: + del self.threads[i] + break + except Queue.Empty: + # no close messages on queue, do nothing + pass + def __calendar_dialog(self, widget, entry): self.dia_confirm.set_modal(False) d = gtk.Window(gtk.WINDOW_TOPLEVEL) @@ -632,9 +707,11 @@ class fpdb: try: self.db = Database.Database(self.config, sql = self.sql) except Exceptions.FpdbMySQLAccessDenied: + #self.db = None self.warning_box("MySQL Server reports: Access denied. Are your permissions set correctly?") exit() except Exceptions.FpdbMySQLNoDatabase: + #self.db = None msg = "MySQL client reports: 2002 or 2003 error. Unable to connect - Please check that the MySQL service has been started" self.warning_box(msg) exit @@ -769,7 +846,6 @@ This program is licensed under the AGPL3, see docs"""+os.sep+"agpl-3.0.txt") self.add_and_display_tab(gv_tab, "Graphs") def __init__(self): - self.threads = [] # no more than 1 process can this lock at a time: self.lock = interlocks.InterProcessLock(name="fpdb_global_lock") self.db = None @@ -793,14 +869,17 @@ This program is licensed under the AGPL3, see docs"""+os.sep+"agpl-3.0.txt") menubar.show() #done menubar + self.threads = [] # objects used by tabs - no need for threads, gtk handles it + self.closeq = Queue.Queue(20) # used to signal ending of a thread (only logviewer for now) + self.nb = gtk.Notebook() self.nb.set_show_tabs(True) self.nb.show() self.main_vbox.pack_start(self.nb, True, True, 0) - self.pages=[] - self.tabs=[] - self.tab_names=[] - self.nb_tabs=[] + self.tabs=[] # the event_boxes forming the actual tabs + self.tab_names=[] # names of tabs used since program started, not removed if tab is closed + self.pages=[] # the contents of the page, not removed if tab is closed + self.nb_tab_names=[] # list of tab names currently displayed in notebook self.tab_main_help(None, None)