From d740acb4e7c11eaf5deb94b35f33f0bfb7ee99be Mon Sep 17 00:00:00 2001 From: Jiri Moskovcak Date: Mon, 23 Feb 2009 16:37:25 +0100 Subject: Added more functionality to gui --- src/Gui/CCDBusBackend.py | 59 ++++++++++++++++++++++++ src/Gui/CCDump.py | 35 ++++++++++++++ src/Gui/CCDumpList.py | 41 +++++++++++++++++ src/Gui/CCGuiDbusBackend.py | 58 ------------------------ src/Gui/CCReporterDialog.py | 56 +++++++++++++++++++++++ src/Gui/CC_gui_functions.py | 39 ++++++++++++++++ src/Gui/ccgui.glade | 108 ++++++++++++++++++++++---------------------- src/Gui/mainwindow.py | 84 ++++++++++++++++++++++++---------- 8 files changed, 345 insertions(+), 135 deletions(-) create mode 100644 src/Gui/CCDBusBackend.py create mode 100644 src/Gui/CCDump.py create mode 100644 src/Gui/CCDumpList.py delete mode 100644 src/Gui/CCGuiDbusBackend.py create mode 100644 src/Gui/CCReporterDialog.py create mode 100644 src/Gui/CC_gui_functions.py (limited to 'src') diff --git a/src/Gui/CCDBusBackend.py b/src/Gui/CCDBusBackend.py new file mode 100644 index 0000000..ad3e82c --- /dev/null +++ b/src/Gui/CCDBusBackend.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python + +import dbus +import gobject +from dbus.mainloop.glib import DBusGMainLoop +import gtk + +CC_IFACE = 'com.redhat.crash_catcher' +CC_PATH = '/com/redhat/crash_catcher' + + +class DBusManager(gobject.GObject): + """ Class to provide communication with daemon over dbus """ + # and later with policyKit + def __init__(self): + gobject.GObject.__init__(self) + # signal emited when new crash is detected + gobject.signal_new ("crash", self ,gobject.SIGNAL_RUN_FIRST,gobject.TYPE_NONE,()) + # binds the dbus to glib mainloop + DBusGMainLoop(set_as_default=True) + self.proxy = None + self.connect_to_daemon() + if self.proxy: + self.cc = dbus.Interface(self.proxy, dbus_interface=CC_IFACE) + #intr = dbus.Interface(proxy, dbus_interface='org.freedesktop.DBus.Introspectable') + self.proxy.connect_to_signal("Crash",self.crash_cb,dbus_interface=CC_IFACE) + else: + raise Exception("Proxy object doesn't exist!") + + # disconnect callback + def disconnected(*args): + print "disconnect" + + def crash_cb(self,*args): + #FIXME "got another crash, gui should reload!" + #for arg in args: + # print arg + #emit a signal + #print "crash" + self.emit("crash") + + def connect_to_daemon(self): + bus = dbus.SystemBus() + if not bus: + raise Exception("Can't connect to dbus") + try: + self.proxy = bus.get_object(CC_IFACE, CC_PATH) + except Exception, e: + raise Exception(e.message + "\nPlease check if crash-catcher daemon is running.") + + def getDumps(self): + row_dict = None + rows = [] + for row in self.cc.GetCrashInfosMap(""): + row_dict = {} + for column in row: + row_dict[column] = row[column] + rows.append(row_dict); + return rows diff --git a/src/Gui/CCDump.py b/src/Gui/CCDump.py new file mode 100644 index 0000000..87474e9 --- /dev/null +++ b/src/Gui/CCDump.py @@ -0,0 +1,35 @@ +from datetime import datetime + +class Dump(): + """Class for mapping the debug dump to pyhon object""" + def __init__(self): + self.UUID = None + self.UID = None + self.Count = None + self.Executable = None + self.Package = None + self.Time = None + + def getUUID(self): + return self.UUID + + def getUID(self): + return self.UID + + def getCount(self): + return self.Count + + def getExecutable(self): + return self.Executable + + def getPackage(self): + return self.Package + + def getTime(self,format): + #print format + if format: + try: + return datetime.fromtimestamp(int(self.Time)).strftime(format) + except Exception, e: + print e + return int(self.Time) diff --git a/src/Gui/CCDumpList.py b/src/Gui/CCDumpList.py new file mode 100644 index 0000000..25e12af --- /dev/null +++ b/src/Gui/CCDumpList.py @@ -0,0 +1,41 @@ +import CCDBusBackend +from CCDump import Dump + +class DumpList(list): + """Class to store list of debug dumps""" + def __init__(self,dbus_manager=None): + self.dm = dbus_manager + + def load(self): + if self.dm: + print "loading DumpList" + try: + rows = self.dm.getDumps() + #print rows + for row in rows: + entry = Dump() + for column in row: + #print "DumpList adding %s:%s" % (column,row[column]) + entry.__dict__[column] = row[column] + self.append(entry) + except Exception, e: + print e + return + else: + print "db == None!" + + +__PFList = None +__PFList_dbmanager = None + +def getDumpList(dbmanager,refresh=None): + global __PFList + global __PFList_dbmanager + + if __PFList == None or refresh or __PFList_dbmanager != dbmanager: + __PFList = DumpList(dbus_manager=dbmanager) + __PFList.load() + __PFList_dbmanager = dbmanager + return __PFList + +__PFList = None diff --git a/src/Gui/CCGuiDbusBackend.py b/src/Gui/CCGuiDbusBackend.py deleted file mode 100644 index 406c1bf..0000000 --- a/src/Gui/CCGuiDbusBackend.py +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env python - -import dbus -import gobject -from dbus.mainloop.glib import DBusGMainLoop -import gtk - -CC_IFACE = 'com.redhat.crash_catcher' -CC_PATH = '/com/redhat/crash_catcher' - - -class DBusManager(gobject.GObject): - """ Class to provide communication with daemon over dbus """ - # and later with policyKit - def __init__(self): - gobject.GObject.__init__(self) - # binds the dbus to glib mainloop - DBusGMainLoop(set_as_default=True) - self.proxy = None - self.connect() - if self.proxy: - self.cc = dbus.Interface(self.proxy, dbus_interface=CC_IFACE) - #intr = dbus.Interface(proxy, dbus_interface='org.freedesktop.DBus.Introspectable') - self.proxy.connect_to_signal("Crash",self.crash_cb,dbus_interface=CC_IFACE) - else: - raise Exception("Proxy object doesn't exist!") - - # disconnect callback - def disconnected(*args): - print "disconnect" - - def crash_cb(self,*args): - print "got another crash while in gui!" - for arg in args: - print arg - #emit a signal - - def connect(self): - bus = dbus.SystemBus() - if not bus: - raise Exception("Can't connect to dbus") - try: - self.proxy = bus.get_object(CC_IFACE, CC_PATH) - except Exception, e: - print "Error while creating the proxy" - print e - - def getDumps(self): - row_dict = None - rows = [] - for row in self.cc.GetCrashInfosMap(""): -# print row - row_dict = {} - for column in row: - row_dict[column] = row[column] - # print "%s:%s" % (column, row[column]) - rows.append(row_dict); - return rows diff --git a/src/Gui/CCReporterDialog.py b/src/Gui/CCReporterDialog.py new file mode 100644 index 0000000..c801de3 --- /dev/null +++ b/src/Gui/CCReporterDialog.py @@ -0,0 +1,56 @@ +import pygtk +pygtk.require("2.0") +import gtk +import gtk.glade +import sys +from CC_gui_functions import * +from CCDumpList import getDumpList, DumpList + +class ReporterDialog(): + """Reporter window""" + def __init__(self, dump): + self.dump = dump + #Set the Glade file + self.gladefile = "ccgui.glade" + self.wTree = gtk.glade.XML(self.gladefile) + #Get the Main Window, and connect the "destroy" event + self.window = self.wTree.get_widget("reporter_dialog") + + #init the dumps treeview + self.tvReport = self.wTree.get_widget("tvReport") + columns = [None]*2 + columns[0] = gtk.TreeViewColumn('Item') + columns[1] = gtk.TreeViewColumn('Value') + + self.reportListStore = gtk.ListStore(str, str, bool) + # set filter + #self.modelfilter = self.reportListStore.filter_new() + #self.modelfilter.set_visible_func(self.filter_dumps, None) + self.tvReport.set_model(self.reportListStore) + renderer = gtk.CellRendererText() + column = gtk.TreeViewColumn('Item', renderer, text=0) + self.tvReport.append_column(column) + + renderer = gtk.CellRendererText() + column = gtk.TreeViewColumn('Value', renderer, text=1, editable=2) + self.tvReport.append_column(column) + + + # connect the signals + self.wTree.get_widget("bApply").connect("clicked", self.on_apply_clicked, self.tvReport) + + self.hydrate() + + def on_apply_clicked(self, button, treeview): + #print treeview + self.window.hide() + + def hydrate(self): + for item in self.dump.__dict__: + self.reportListStore.append([item, self.dump.__dict__[item], False]) + self.reportListStore.append(["Comment","", True]) + + def run(self): + self.window.show() + + diff --git a/src/Gui/CC_gui_functions.py b/src/Gui/CC_gui_functions.py new file mode 100644 index 0000000..5ac8e8c --- /dev/null +++ b/src/Gui/CC_gui_functions.py @@ -0,0 +1,39 @@ +import gtk + +def gui_error_message ( message, parent_dialog=None, + message_type=gtk.MESSAGE_ERROR, + widget=None, page=0, broken_widget=None ): + + dialog = gtk.MessageDialog( parent_dialog, + gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT, + message_type, gtk.BUTTONS_OK, + message ) + + if parent_dialog: + dialog.set_position (gtk.WIN_POS_CENTER_ON_PARENT) + dialog.set_transient_for(parent_dialog) + else: + dialog.set_position (gtk.WIN_POS_CENTER) + + ret = dialog.run () + dialog.destroy() + return ret + +def gui_info_dialog ( message, parent_dialog=None, + message_type=gtk.MESSAGE_INFO, + widget=None, page=0, broken_widget=None ): + + dialog = gtk.MessageDialog( parent_dialog, + gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT, + message_type, gtk.BUTTONS_YES_NO, + message ) + + if parent_dialog: + dialog.set_position (gtk.WIN_POS_CENTER_ON_PARENT) + dialog.set_transient_for(parent_dialog) + else: + dialog.set_position (gtk.WIN_POS_CENTER) + + ret = dialog.run () + dialog.destroy() + return ret diff --git a/src/Gui/ccgui.glade b/src/Gui/ccgui.glade index 3623c15..61c2981 100644 --- a/src/Gui/ccgui.glade +++ b/src/Gui/ccgui.glade @@ -1,6 +1,6 @@ - + 640 @@ -336,7 +336,7 @@ - + True 2 @@ -348,7 +348,7 @@ - + 5 GTK_WIN_POS_CENTER_ON_PARENT 400 @@ -365,14 +365,14 @@ True - label + Report False - + True True @@ -390,7 +390,7 @@ True GTK_BUTTONBOX_END - + True True True @@ -400,7 +400,7 @@ - + True True True @@ -458,53 +458,6 @@ - - - True - BlackList - - - - - True - True - - - 1 - 2 - - - - - True - Database Plugin - - - 1 - 2 - - - - - True - - - 1 - 2 - 1 - 2 - - - - - True - OpenPGP Public Keys - - - 2 - 3 - - True @@ -553,6 +506,53 @@ 3 + + + True + OpenPGP Public Keys + + + 2 + 3 + + + + + True + + + 1 + 2 + 1 + 2 + + + + + True + Database Plugin + + + 1 + 2 + + + + + True + True + + + 1 + 2 + + + + + True + BlackList + + diff --git a/src/Gui/mainwindow.py b/src/Gui/mainwindow.py index 81d7385..b333379 100755 --- a/src/Gui/mainwindow.py +++ b/src/Gui/mainwindow.py @@ -5,17 +5,26 @@ import pygtk pygtk.require("2.0") import gtk import gtk.glade -import CCGuiDbusBackend -from datetime import datetime +import CCDBusBackend +import sys +from CC_gui_functions import * +from CCDumpList import getDumpList, DumpList +from CCReporterDialog import ReporterDialog def cb(self, *args): pass -class CCMainWindow(): +class MainWindow(): """This is an Hello World GTK application""" def __init__(self): - self.ccdaemon = CCGuiDbusBackend.DBusManager() + try: + self.ccdaemon = CCDBusBackend.DBusManager() + except Exception, e: + # show error message if connection fails + # FIXME add an option to start the daemon + gui_error_message(e.message) + sys.exit() #Set the Glade file self.gladefile = "ccgui.glade" self.wTree = gtk.glade.XML(self.gladefile) @@ -26,13 +35,15 @@ class CCMainWindow(): if (self.window): self.window.connect("destroy", gtk.main_quit) + self.appBar = self.wTree.get_widget("appBar") + #init the dumps treeview self.dlist = self.wTree.get_widget("tvDumps") columns = [None]*2 columns[0] = gtk.TreeViewColumn('Date') - columns[1] = gtk.TreeViewColumn('package') + columns[1] = gtk.TreeViewColumn('Package') # create list - self.dumpsListStore = gtk.ListStore(str, str, int) + self.dumpsListStore = gtk.ListStore(str, str, object) # set filter self.modelfilter = self.dumpsListStore.filter_new() self.modelfilter.set_visible_func(self.filter_dumps, None) @@ -49,7 +60,29 @@ class CCMainWindow(): self.wTree.get_widget("bDelete").connect("clicked", self.on_bDelete_clicked) self.wTree.get_widget("bNext").connect("clicked", self.on_bNext_clicked) self.wTree.get_widget("bQuit").connect("clicked", self.on_bQuit_clicked) + self.ccdaemon.connect("crash", self.on_data_changed_cb, None) + # load data + self.load() + + def load(self): + self.appBar.push(0,"Loading dumps...") + self.loadDumpList() + self.appBar.pop(0) + + def loadDumpList(self): + #dumplist = getDumpList(dbmanager=self.ccdaemon) + pass + + def on_data_changed_cb(self, *args): + ret = gui_info_dialog("Another crash detected, do you want to refresh the data?",self.window) + if ret == gtk.RESPONSE_YES: + self.hydrate() + else: + pass + #print "got another crash, refresh gui?" + + def filter_dumps(self, model, miter, data): # this could be use for filtering the dumps return True @@ -58,45 +91,50 @@ class CCMainWindow(): self.window.show() def hydrate(self): - self.rows = self.ccdaemon.getDumps() - row_c = 0 - for row in self.rows: - self.dumpsListStore.append([row["Time"], row["Package"], row_c]) - row_c += 1 + self.dumpsListStore.clear() + dumplist = getDumpList(self.ccdaemon, refresh=True) + #self.rows = self.ccdaemon.getDumps() + #row_c = 0 + for entry in dumplist: + self.dumpsListStore.append([entry.getTime("%Y:%m:%d"),entry.getPackage(),entry]) + #row_c += 1 def on_tvDumps_cursor_changed(self,treeview): dumpsListStore, path = self.dlist.get_selection().get_selected_rows() if not path: return - # rewrite this OO - #DumpList class - row = self.rows[dumpsListStore.get_value(dumpsListStore.get_iter(path[0]), 2)] + # this should work until we keep the row object in the last position + dump = dumpsListStore.get_value(dumpsListStore.get_iter(path[0]), len(dumpsListStore)) lDate = self.wTree.get_widget("lDate") #move this to Dump class - t = datetime.fromtimestamp(int(row["Time"])) - date = t.strftime("%Y-%m-%d %H:%M:%S") - lDate.set_label(date) + lDate.set_label(dump.getTime("%Y.%m.%d %H:%M:%S")) lPackage = self.wTree.get_widget("lPackage") - lPackage.set_label(row["Package"]) - self.wTree.get_widget("lExecutable").set_label(row["Executable"]) - self.wTree.get_widget("lCRate").set_label(row["Count"]) + lPackage.set_label(dump.getPackage()) + self.wTree.get_widget("lExecutable").set_label(dump.getExecutable()) + self.wTree.get_widget("lCRate").set_label(dump.getCount()) #print self.rows[row] def on_bDelete_clicked(self, button): print "Delete" def on_bNext_clicked(self, button): - print "Next" + # FIXME don't duplicate the code, move to function + dumpsListStore, path = self.dlist.get_selection().get_selected_rows() + if not path: + return + dump = dumpsListStore.get_value(dumpsListStore.get_iter(path[0]), len(dumpsListStore)) + # show the report window with selected dump + report_dialog = ReporterDialog(dump) + report_dialog.run() def on_bQuit_clicked(self, button): - print "Quit" gtk.main_quit() if __name__ == "__main__": - cc = CCMainWindow() + cc = MainWindow() cc.hydrate() cc.show() gtk.main() -- cgit