From b413cf38cdcbae6aede174aa54103181395f7bbc Mon Sep 17 00:00:00 2001 From: Jiri Moskovcak Date: Mon, 2 Mar 2009 15:30:17 +0100 Subject: New gui DBusBackend improvements --- src/Gui/CCDBusBackend.py | 22 +- src/Gui/CCDump.py | 6 + src/Gui/CCDumpList.py | 2 + src/Gui/CCMainWindow.py | 163 ++++++---- src/Gui/CCReporterDialog.py | 2 +- src/Gui/CC_gui_functions.py | 59 +++- src/Gui/Makefile.am | 2 +- src/Gui/ccgui.glade | 774 ++++---------------------------------------- src/Gui/report.glade | 86 +++++ 9 files changed, 334 insertions(+), 782 deletions(-) create mode 100644 src/Gui/report.glade (limited to 'src') diff --git a/src/Gui/CCDBusBackend.py b/src/Gui/CCDBusBackend.py index f1d6ed1..0408df9 100644 --- a/src/Gui/CCDBusBackend.py +++ b/src/Gui/CCDBusBackend.py @@ -14,6 +14,8 @@ class DBusManager(gobject.GObject): gobject.GObject.__init__(self) # signal emited when new crash is detected gobject.signal_new ("crash", self ,gobject.SIGNAL_RUN_FIRST,gobject.TYPE_NONE,()) + # signal emited when new analyze is complete + gobject.signal_new ("analyze-complete", self ,gobject.SIGNAL_RUN_FIRST,gobject.TYPE_NONE,(gobject.TYPE_PYOBJECT,)) # binds the dbus to glib mainloop DBusGMainLoop(set_as_default=True) self.proxy = None @@ -21,7 +23,10 @@ class DBusManager(gobject.GObject): if self.proxy: self.cc = dbus.Interface(self.proxy, dbus_interface=CC_IFACE) #intr = dbus.Interface(proxy, dbus_interface='org.freedesktop.DBus.Introspectable') + # new crash notify self.proxy.connect_to_signal("Crash",self.crash_cb,dbus_interface=CC_IFACE) + # BT extracting complete + self.proxy.connect_to_signal("AnalyzeComplete",self.analyze_complete_cb,dbus_interface=CC_IFACE) else: raise Exception("Proxy object doesn't exist!") @@ -37,6 +42,13 @@ class DBusManager(gobject.GObject): #print "crash" self.emit("crash") + def analyze_complete_cb(self,*args): + for arg in args: + print "Analyze complete for: %s" % arg + # emit signal to let clients know that analyze has been completed + # maybe rewrite this with async method call? + self.emit("analyze-complete", arg) + def connect_to_daemon(self): bus = dbus.SystemBus() if not bus: @@ -47,11 +59,17 @@ class DBusManager(gobject.GObject): raise Exception(e.message + "\nPlease check if crash-catcher daemon is running.") def getReport(self, UUID): - return self.cc.CreateReport(UUID) + try: + return self.cc.CreateReport(UUID) + except dbus.exceptions.DBusException, e: + raise Exception(e.message) def Report(self,report): return self.cc.Report(report) - + + def DeleteDebugDump(self,UUID): + return self.cc.DeleteDebugDump(UUID) + def getDumps(self): row_dict = None rows = [] diff --git a/src/Gui/CCDump.py b/src/Gui/CCDump.py index 5e2e651..38fc369 100644 --- a/src/Gui/CCDump.py +++ b/src/Gui/CCDump.py @@ -34,3 +34,9 @@ class Dump(): except Exception, e: print e return int(self.Time) + + def getPackageName(self): + return self.Package[:self.Package.find("-")] + + def getDescription(self): + return self.Description diff --git a/src/Gui/CCDumpList.py b/src/Gui/CCDumpList.py index 5aaae92..ecc43fe 100644 --- a/src/Gui/CCDumpList.py +++ b/src/Gui/CCDumpList.py @@ -5,6 +5,7 @@ class DumpList(list): """Class to store list of debug dumps""" def __init__(self,dbus_manager=None): self.dm = dbus_manager + self.ddict = {} def load(self): if self.dm: @@ -18,6 +19,7 @@ class DumpList(list): #print "DumpList adding %s:%s" % (column,row[column]) entry.__dict__[column] = row[column] self.append(entry) + self.ddict[entry.UUID] = entry except Exception, e: print e return diff --git a/src/Gui/CCMainWindow.py b/src/Gui/CCMainWindow.py index 0c589f6..4c35afa 100644 --- a/src/Gui/CCMainWindow.py +++ b/src/Gui/CCMainWindow.py @@ -9,11 +9,14 @@ from CC_gui_functions import * from CCDumpList import getDumpList, DumpList from CCReporterDialog import ReporterDialog from CCReport import Report +try: + import rpm +except: + rpm = None -def cb(self, *args): - pass class MainWindow(): def __init__(self): + self.theme = theme = gtk.icon_theme_get_default() try: self.ccdaemon = CCDBusBackend.DBusManager() except Exception, e: @@ -27,96 +30,119 @@ class MainWindow(): self.wTree = gtk.glade.XML(self.gladefile) #Get the Main Window, and connect the "destroy" event - self.window = self.wTree.get_widget("main_window") - # self.window.set_default_size(640, 480) + self.window = self.wTree.get_widget("main_window2") + self.window.set_default_size(700, 480) if (self.window): - self.window.connect("destroy", gtk.main_quit) + self.window.connect("delete_event", self.delete_event_cb) + self.window.connect("destroy", self.destroy) self.appBar = self.wTree.get_widget("appBar") + # set colours for descritpion heading + self.wTree.get_widget("evDescription").modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse("black")) + #init the dumps treeview self.dlist = self.wTree.get_widget("tvDumps") - columns = [None]*2 - columns[0] = gtk.TreeViewColumn('Date') - columns[1] = gtk.TreeViewColumn('Package') - # create list - self.dumpsListStore = gtk.ListStore(str, str, object) + self.dumpsListStore = gtk.ListStore(gtk.gdk.Pixbuf, str,str,str,str, object) # set filter self.modelfilter = self.dumpsListStore.filter_new() self.modelfilter.set_visible_func(self.filter_dumps, None) self.dlist.set_model(self.modelfilter) + # add pixbuff separatelly + icon_column = gtk.TreeViewColumn('Icon') + icon_column.cell = gtk.CellRendererPixbuf() + n = self.dlist.append_column(icon_column) + icon_column.pack_start(icon_column.cell, False) + icon_column.set_attributes(icon_column.cell, pixbuf=(n-1)) + # =============================================== + columns = [None]*4 + columns[0] = gtk.TreeViewColumn('Package') + columns[1] = gtk.TreeViewColumn('Application') + columns[2] = gtk.TreeViewColumn('Date') + columns[3] = gtk.TreeViewColumn('Crash Rate') + # create list for column in columns: n = self.dlist.append_column(column) column.cell = gtk.CellRendererText() column.pack_start(column.cell, False) column.set_attributes(column.cell, text=(n-1)) column.set_resizable(True) - #connect signals self.dlist.connect("cursor-changed", self.on_tvDumps_cursor_changed) - 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("bDelete").connect("clicked", self.on_bDelete_clicked, self.dlist) + self.wTree.get_widget("bReport").connect("clicked", self.on_bReport_clicked) self.wTree.get_widget("bQuit").connect("clicked", self.on_bQuit_clicked) self.ccdaemon.connect("crash", self.on_data_changed_cb, None) + self.ccdaemon.connect("analyze-complete", self.on_analyze_complete_cb) # 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 - - def show(self): - self.window.show() - + #self.load() def hydrate(self): self.dumpsListStore.clear() + self.rows = self.ccdaemon.getDumps() dumplist = getDumpList(self.ccdaemon, refresh=True) - #self.rows = self.ccdaemon.getDumps() - #row_c = 0 for entry in dumplist: - self.dumpsListStore.append([entry.getTime("%m.%d."),entry.getPackage(),entry]) - #row_c += 1 - + icon = get_icon_for_package(self.theme,entry.getPackageName()) + self.dumpsListStore.append([icon, entry.getPackage(),entry.getExecutable(), entry.getTime("%Y.%m.%d %H:%M:%S"),entry.getCount(), entry]) + + def filter_dumps(self, model, miter, data): + # for later.. + return True + def on_tvDumps_cursor_changed(self,treeview): dumpsListStore, path = self.dlist.get_selection().get_selected_rows() if not path: + self.wTree.get_widget("lDescription").set_label("") return - # this should work until we keep the row object in the last position dump = dumpsListStore.get_value(dumpsListStore.get_iter(path[0]), len(self.dlist.get_columns())) - lDate = self.wTree.get_widget("lDate") #move this to Dump class - lDate.set_label(dump.getTime("%Y.%m.%d %H:%M:%S")) lPackage = self.wTree.get_widget("lPackage") - lPackage.set_label(dump.getPackage()) - self.wTree.get_widget("lExecutable").set_label(dump.getExecutable()) - self.wTree.get_widget("lCRate").set_label(dump.getCount()) + self.wTree.get_widget("lDescription").set_label(dump.getDescription()) #print self.rows[row] + def on_bDelete_clicked(self, button, treeview): + dumpsListStore, path = self.dlist.get_selection().get_selected_rows() + if not path: + return + # this should work until we keep the row object in the last position + dump = dumpsListStore.get_value(dumpsListStore.get_iter(path[0]), len(self.dlist.get_columns())) + try: + if self.ccdaemon.DeleteDebugDump(dump.UUID): + self.hydrate() + treeview.emit("cursor-changed") + else: + print "Couldn't delete" + except Exception, e: + print e + + def destroy(self, widget, data=None): + print "destroy signal occurred" + gtk.main_quit() + + def on_data_changed_cb(self, *args): + ret = gui_question_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 on_analyze_complete_cb(self, daemon, UUID): + dumplist = getDumpList(self.ccdaemon) + entry = dumplist.ddict[UUID] + print "GUI: Analyze for package %s crash with UUID %s is complete" % (entry.Package, UUID) + print "We should refresh the UI ..." - def on_bDelete_clicked(self, button): - print "Delete" - - def on_bNext_clicked(self, button): + #ret = gui_question_dialog("GUI: Analyze for package %s crash with UUID %s is complete" % (entry.Package, UUID),self.window) + #if ret == gtk.RESPONSE_YES: + # self.hydrate() + #else: + # pass + #print "got another crash, refresh gui?" + + + def on_bReport_clicked(self, button): # FIXME don't duplicate the code, move to function dumpsListStore, path = self.dlist.get_selection().get_selected_rows() if not path: @@ -125,10 +151,10 @@ class MainWindow(): # show the report window with selected dump try: report = self.ccdaemon.getReport(dump.getUUID()) - except Exception,e: + except Exception, e: # FIXME #3 dbus.exceptions.DBusException: org.freedesktop.DBus.Error.NoReply: Did not receive a reply # do this async and wait for yum to end with debuginfoinstal - gui_error_message("Operation taking too long - \nPlease try again after debuginfo is installed") + gui_error_message("Error getting the report: %s" % e.message) return if not report: @@ -136,14 +162,25 @@ class MainWindow(): return report_dialog = ReporterDialog(report) result = report_dialog.run() - if result == gtk.RESPONSE_CANCEL: - pass - else: + if result == gtk.RESPONSE_APPLY: self.ccdaemon.Report(result) - - def on_bQuit_clicked(self, button): - gtk.main_quit() + + + def delete_event_cb(self, widget, event, data=None): + # Change FALSE to TRUE and the main window will not be destroyed + # with a "delete_event". + return self.on_bQuit_clicked(widget) + def on_bQuit_clicked(self, widget): + ret = gui_question_dialog("Do you really want to quit?",self.window) + if ret == gtk.RESPONSE_YES: + gtk.main_quit() + return True + + def show(self): + self.window.show() + + if __name__ == "__main__": cc = MainWindow() cc.hydrate() diff --git a/src/Gui/CCReporterDialog.py b/src/Gui/CCReporterDialog.py index 4119d11..91f6961 100644 --- a/src/Gui/CCReporterDialog.py +++ b/src/Gui/CCReporterDialog.py @@ -13,7 +13,7 @@ class ReporterDialog(): self.report = report #Set the Glade file # FIXME add to path - self.gladefile = "/usr/share/crash-catcher/ccgui.glade" + self.gladefile = "/usr/share/crash-catcher/report.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") diff --git a/src/Gui/CC_gui_functions.py b/src/Gui/CC_gui_functions.py index 5ac8e8c..703ec25 100644 --- a/src/Gui/CC_gui_functions.py +++ b/src/Gui/CC_gui_functions.py @@ -1,4 +1,9 @@ import gtk +try: + # we don't want to add dependency to rpm, but if we have it, we can use it + import rpm +except: + rpm = None def gui_error_message ( message, parent_dialog=None, message_type=gtk.MESSAGE_ERROR, @@ -19,8 +24,8 @@ def gui_error_message ( message, parent_dialog=None, dialog.destroy() return ret -def gui_info_dialog ( message, parent_dialog=None, - message_type=gtk.MESSAGE_INFO, +def gui_question_dialog ( message, parent_dialog=None, + message_type=gtk.MESSAGE_QUESTION, widget=None, page=0, broken_widget=None ): dialog = gtk.MessageDialog( parent_dialog, @@ -37,3 +42,53 @@ def gui_info_dialog ( message, parent_dialog=None, ret = dialog.run () dialog.destroy() return ret + +def get_icon_for_package(theme,package): + #print package + try: + return theme.load_icon(package, 22, gtk.ICON_LOOKUP_USE_BUILTIN) + except: + # try to find icon filename by manually + if not rpm: + return None + ts = rpm.TransactionSet() + mi = ts.dbMatch( 'name', package ) + possible_icons = [] + icon_filename = "" + filenames = "" + for h in mi: + filenames = h['filenames'] + for filename in filenames: + # add check only for last 4 chars + if filename.rfind(".png") != -1: + possible_icons.append(filename) + if filename.rfind(".desktop") != -1: + #print filename + desktop_file = open(filename, 'r') + lines = desktop_file.readlines() + for line in lines: + if line.find("Icon=") != -1: + #print line[5:-1] + icon_filename = line[5:-1] + break + desktop_file.close() + # .dektop file found + for filename in h['filenames']: + if filename.rfind("%s.png" % icon_filename) != -1: + #print filename + icon_filename = filename + break + #we didn't find the .desktop file + else: + for filename in possible_icons: + if filename.rfind("%s.png" % package): + # return the first possible filename + icon_filename = filename + break + if icon_filename: + break + if icon_filename: + #print "icon created form %s" % icon_filename + return gtk.gdk.pixbuf_new_from_file_at_size(icon_filename,22,22) + else: + return None diff --git a/src/Gui/Makefile.am b/src/Gui/Makefile.am index a4d20b7..eea12e0 100644 --- a/src/Gui/Makefile.am +++ b/src/Gui/Makefile.am @@ -2,7 +2,7 @@ bin_SCRIPTS = cc-gui -PYTHON_FILES = CCDBusBackend.py CCDumpList.py CCDump.py CC_gui_functions.py ccgui.glade CCReporterDialog.py CCReport.py CCMainWindow.py +PYTHON_FILES = CCDBusBackend.py CCDumpList.py CCDump.py CC_gui_functions.py ccgui.glade report.glade CCReporterDialog.py CCReport.py CCMainWindow.py GLADE_FILES = ccgui.glade diff --git a/src/Gui/ccgui.glade b/src/Gui/ccgui.glade index 8fbd946..dbac11d 100644 --- a/src/Gui/ccgui.glade +++ b/src/Gui/ccgui.glade @@ -1,713 +1,7 @@ - + - - 640 - 480 - - - True - - - True - - - True - _File - True - - - True - - - True - gtk-new - True - True - - - - - True - gtk-open - True - True - - - - - True - gtk-save - True - True - - - - - True - gtk-save-as - True - True - - - - - True - - - - - True - gtk-quit - True - True - - - - - - - - - True - _Edit - True - - - True - - - True - gtk-cut - True - True - - - - - True - gtk-copy - True - True - - - - - True - gtk-paste - True - True - - - - - True - gtk-delete - True - True - - - - - - - - - True - _View - True - - - - - True - _Help - True - - - True - - - True - gtk-about - True - True - - - - - - - - - False - - - - - True - - - True - True - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC - - - True - True - - - - - - - - True - True - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC - - - True - - - True - - - True - True - - - True - <b>Date:</b> - True - - - - - True - - - 1 - - - - - - - True - True - - - True - <b>Package:</b> - True - - - - - True - - - 1 - - - - - 1 - - - - - True - Package-info-placeholder - - - 2 - - - - - True - True - - - True - <b>Executable:</b> - True - - - - - True - - - 1 - - - - - 3 - - - - - True - True - - - True - <b>Crash rate:</b> - True - - - - - True - - - 1 - - - - - 4 - - - - - - - True - - - True - True - True - gtk-delete - True - 0 - - - - - True - True - True - gtk-go-forward - True - 0 - - - 1 - - - - - True - True - True - gtk-quit - True - 0 - - - 2 - - - - - False - 1 - - - - - - - 1 - - - - - 1 - - - - - True - 2 - - - False - 2 - - - - - - - 5 - GTK_WIN_POS_CENTER_ON_PARENT - 400 - 400 - GDK_WINDOW_TYPE_HINT_DIALOG - False - - - True - 2 - - - True - - - True - Report - - - False - - - - - True - True - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC - - - True - True - - - - - 1 - - - - - 1 - - - - - True - GTK_BUTTONBOX_END - - - True - True - True - gtk-apply - True - -10 - - - - - True - True - True - gtk-cancel - True - -6 - - - 1 - - - - - False - GTK_PACK_END - - - - - - - 5 - GTK_WIN_POS_CENTER_ON_PARENT - 400 - GDK_WINDOW_TYPE_HINT_DIALOG - False - - - True - 2 - - - True - True - - - True - 6 - 2 - - - - - - - - - - - - - - - - - - - - - True - BlackList - - - - - True - True - - - 1 - 2 - - - - - True - Database Plugin - - - 1 - 2 - - - - - True - - - 1 - 2 - 1 - 2 - - - - - True - OpenPGP Public Keys - - - 2 - 3 - - - - - True - - - True - True - - - - - True - - - True - True - True - gtk-add - True - 0 - - - - - True - True - True - gtk-remove - True - 0 - - - 1 - - - - - 1 - - - - - 1 - 2 - 2 - 3 - - - - - - - True - Settings - - - tab - False - - - - - True - - - True - 10 - - - True - True - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC - - - True - True - - - - - - - True - True - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC - - - True - - - True - reporter association - - - False - - - - - True - True - - - 1 - - - - - - - 1 - - - - - - - True - True - - - - - - True - Plugin details - - - label_item - - - - - 1 - - - - - 1 - - - - - True - Plugins - - - tab - 1 - False - - - - - 1 - - - - - True - GTK_BUTTONBOX_END - - - True - True - True - gtk-apply - True - 0 - - - - - True - True - True - gtk-cancel - True - 0 - - - 1 - - - - - False - GTK_PACK_END - - - - - Crash-catcher GTK_WIN_POS_CENTER @@ -717,6 +11,8 @@ True + False + Not implemented yet... True @@ -859,7 +155,7 @@ 10 True - + True True True @@ -869,11 +165,12 @@ - + True True True - button + gtk-save + True 0 @@ -906,7 +203,7 @@ - + True True True @@ -930,14 +227,65 @@ - + True True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + + + True + True + + 2 + + + True + + + True + 0.05000000074505806 + <span color="white">Description</span> + True + + + + + False + 3 + + + + + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + + + True + GTK_RESIZE_QUEUE + + + True + 0.10000000149011612 + 0.20000000298023224 + GTK_JUSTIFY_FILL + True + + + + + + + 4 + + True @@ -945,7 +293,7 @@ False - 3 + 5 diff --git a/src/Gui/report.glade b/src/Gui/report.glade new file mode 100644 index 0000000..d8bc9bb --- /dev/null +++ b/src/Gui/report.glade @@ -0,0 +1,86 @@ + + + + + + 5 + GTK_WIN_POS_CENTER_ON_PARENT + 400 + 400 + GDK_WINDOW_TYPE_HINT_DIALOG + False + + + True + 2 + + + True + + + True + Report + + + False + + + + + True + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + + + True + True + + + + + 1 + + + + + 1 + + + + + True + GTK_BUTTONBOX_END + + + True + True + True + gtk-apply + True + -10 + + + + + True + True + True + gtk-cancel + True + -6 + + + 1 + + + + + False + GTK_PACK_END + + + + + + -- cgit