From 892b7fde75cafca50a18dcb8620ddc22b2e74688 Mon Sep 17 00:00:00 2001 From: Jiri Moskovcak Date: Thu, 4 Mar 2010 14:57:51 +0100 Subject: GUI: added reporter selection window --- src/Gui/CCDump.py | 4 + src/Gui/CCDumpList.py | 2 +- src/Gui/CCMainWindow.py | 126 +++---------------------- src/Gui/CCReporterDialog.py | 223 +++++++++++++++++++++++++++++++++++++++++++- src/Gui/PluginList.py | 7 ++ src/Gui/ccgui.glade | 70 -------------- src/Gui/report.glade | 136 +++++++++++++++++++++++++++ 7 files changed, 382 insertions(+), 186 deletions(-) (limited to 'src') diff --git a/src/Gui/CCDump.py b/src/Gui/CCDump.py index 2bb55dd0..3d6e8704 100644 --- a/src/Gui/CCDump.py +++ b/src/Gui/CCDump.py @@ -54,6 +54,7 @@ class Dump(): self.description = None self.Message = None self.Reported = None + self.analyzer = None def getUUID(self): return self.UUID[CD_CONTENT] @@ -93,3 +94,6 @@ class Dump(): def getDescription(self): return self.description[CD_CONTENT] + + def getAnalyzerName(self): + return self.analyzer[CD_CONTENT] diff --git a/src/Gui/CCDumpList.py b/src/Gui/CCDumpList.py index 9d6bb2a2..f3df21ed 100644 --- a/src/Gui/CCDumpList.py +++ b/src/Gui/CCDumpList.py @@ -18,7 +18,7 @@ class DumpList(list): for row in rows: entry = Dump() for column in row: - log2(" DumpList.%s='%s'", column, row[column]) + log2(" Dump.%s='%s'", column, row[column]) entry.__dict__[column] = row[column] self.append(entry) except Exception: diff --git a/src/Gui/CCMainWindow.py b/src/Gui/CCMainWindow.py index 4a8329d8..74291fb2 100644 --- a/src/Gui/CCMainWindow.py +++ b/src/Gui/CCMainWindow.py @@ -2,7 +2,6 @@ import sys import pwd import getopt -from glib import markup_escape_text from abrt_utils import _, init_logging, log, log1, log2 import gobject @@ -22,7 +21,7 @@ import CCDBusBackend from CC_gui_functions import * from CCDumpList import getDumpList from CCDump import * # FILENAME_xxx, CD_xxx -from CCReporterDialog import ReporterDialog +from CCReporterDialog import ReporterDialog, ReporterSelector from PluginsSettingsDialog import PluginsSettingsDialog from SettingsDialog import SettingsDialog from PluginList import getPluginInfoList @@ -55,13 +54,6 @@ class MainWindow(): self.window.connect("destroy", self.destroy) self.window.connect("focus-in-event", self.focus_in_cb) - # pregress bar window to show while bt is being extracted - self.pBarWindow = self.wTree.get_widget("pBarWindow") - if self.pBarWindow: - self.pBarWindow.connect("delete_event", self.sw_delete_event_cb) - self.pBarWindow.set_transient_for(self.window) - self.pBar = self.wTree.get_widget("pBar") - #init the dumps treeview self.dlist = self.wTree.get_widget("tvDumps") #rows of items with: @@ -120,19 +112,19 @@ class MainWindow(): self.wTree.get_widget("miDelete").connect("activate", self.on_bDelete_clicked, self.dlist) # connect handlers for daemon signals self.ccdaemon.connect("crash", self.on_data_changed_cb, None) - self.ccdaemon.connect("analyze-complete", self.on_analyze_complete_cb, self.pBarWindow) self.ccdaemon.connect("abrt-error", self.error_cb) - self.ccdaemon.connect("update", self.update_cb) + #self.ccdaemon.connect("update", self.update_cb) # for now, just treat them the same (w/o this, we don't even see daemon warnings in logs!): - self.ccdaemon.connect("warning", self.update_cb) + #self.ccdaemon.connect("warning", self.update_cb) self.ccdaemon.connect("show", self.show_cb) self.ccdaemon.connect("daemon-state-changed", self.on_daemon_state_changed_cb) self.ccdaemon.connect("report-done", self.on_report_done_cb) - # load data - #self.load() self.pluginlist = None + def on_report_done_cb(self, daemon, result): + self.hydrate() + def on_daemon_state_changed_cb(self, widget, state): if state == "up": self.hydrate() # refresh crash list @@ -161,18 +153,12 @@ class MainWindow(): dialog = SettingsDialog(self.window, self.ccdaemon) try: dialog.hydrate() - except Exception, e: - gui_error_message(_("Can't show the settings dialog\n%s" % e)) + except Exception, ex: + gui_error_message(_("Can't show the settings dialog\n%s" % ex)) return dialog.show() def error_cb(self, daemon, message=None): - # try to hide the progressbar, we dont really care if it was visible .. - try: - gobject.source_remove(self.timer) - self.pBarWindow.hide() - except Exception: - pass gui_error_message(_("Unable to finish current task!\n%s" % message), parent_dialog=self.window) def update_cb(self, daemon, message): @@ -188,11 +174,6 @@ class MainWindow(): tvUpdates.set_buffer(buff) tvUpdates.scroll_mark_onscreen(end) - # call to update the progressbar - def progress_update_cb(self, *args): - self.pBar.pulse() - return True - def hydrate(self): n = None self.dumpsListStore.clear() @@ -272,7 +253,7 @@ class MainWindow(): def destroy(self, widget, data=None): gtk.main_quit() - def on_data_changed_cb(self, *args): + def on_data_changed_cb(self, *_args): # FIXME mark the new entry somehow.... # remember the selected row dumpsListStore, path = self.dlist.get_selection().get_selected_rows() @@ -281,101 +262,18 @@ class MainWindow(): return self.dlist.set_cursor(path[0]) - def on_report_done_cb(self, daemon, result): - try: - gobject.source_remove(self.timer) - except: - pass - self.pBarWindow.hide() - gui_report_dialog(result, self.window) - self.hydrate() - - def on_analyze_complete_cb(self, daemon, report, pBarWindow): - try: - gobject.source_remove(self.timer) - except: - pass - self.pBarWindow.hide() -#FIXME - why we need this?? -> timeout warnings -# try: -# dumplist = getDumpList(self.ccdaemon) -# except Exception, e: -# print e - if not report: - gui_error_message(_("Unable to get report!\nDebuginfo is missing?")) - return - report_dialog = ReporterDialog(report, self.ccdaemon, log=self.updates, parent=self.window) - # (response, report) - response, result = report_dialog.run() - - if response == gtk.RESPONSE_APPLY: - try: - self.pBarWindow.show_all() - self.timer = gobject.timeout_add(100, self.progress_update_cb) - pluginlist = getPluginInfoList(self.ccdaemon) - reporters_settings = pluginlist.getReporterPluginsSettings() - log2("Report(result,reporters,settings):") - log2(" result:%s", str(result)) - # Careful, this will print reporters_settings["Password"] too - log2(" settings:%s", str(reporters_settings)) - self.ccdaemon.Report(result, ["reporter1", "reporter2"], reporters_settings) - log2("Report() returned") - #self.hydrate() - except Exception, ex: - gui_error_message(_("Reporting failed!\n%s" % ex)) - # -50 == REFRESH - elif response == -50: - self.refresh_report(report) - - def refresh_report(self, report): - self.updates = "" - self.pBarWindow.show_all() - self.timer = gobject.timeout_add(100, self.progress_update_cb) - - # show the report window with selected report - try: - self.ccdaemon.getReport(report[CD_UUID][CD_CONTENT], force=1) - except Exception, ex: - # 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 - if self.timer: - gobject.source_remove(self.timer) - self.pBarWindow.hide() - gui_error_message(_("Error getting the report: %s" % ex)) - return - def on_bReport_clicked(self, button): dumpsListStore, path = self.dlist.get_selection().get_selected_rows() self.on_dumpRowActivated(self.dlist, None, path, None) - def on_dumpRowActivated(self, treeview, iter, path, user_data=None): - self.updates = "" - # FIXME don't duplicate the code, move to function + def on_dumpRowActivated(self, treeview, it, path, user_data=None): dumpsListStore, path = treeview.get_selection().get_selected_rows() if not path: return - #self.pBar.show() - self.pBarWindow.show_all() - self.timer = gobject.timeout_add(100, self.progress_update_cb) - dump = dumpsListStore.get_value(dumpsListStore.get_iter(path[0]), dumpsListStore.get_n_columns()-1) - # show the report window with selected dump - try: - self.ccdaemon.getReport(dump.getUUID()) - except Exception, ex: - # 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 - if self.timer: - gobject.source_remove(self.timer) - self.pBarWindow.hide() - gui_error_message(_("Error getting the report: %s" % ex)) - return - def sw_delete_event_cb(self, widget, event, data=None): - if self.timer: - gobject.source_remove(self.timer) - widget.hide() - return True + rs = ReporterSelector(dump, self.ccdaemon, parent=self.window) + rs.show() def delete_event_cb(self, widget, event, data=None): gtk.main_quit() diff --git a/src/Gui/CCReporterDialog.py b/src/Gui/CCReporterDialog.py index 989f7cbe..7cbe2197 100644 --- a/src/Gui/CCReporterDialog.py +++ b/src/Gui/CCReporterDialog.py @@ -2,8 +2,8 @@ import pygtk pygtk.require("2.0") import gtk +import gobject import gtk.glade -import pango import sys from CC_gui_functions import * import CellRenderers @@ -326,3 +326,224 @@ class ReporterDialog(): result = self.window.run() self.window.destroy() return (result, self.report) + +class ReporterSelector(): + def __init__(self, crashdump, daemon, log=None, parent=None): + self.connected_signals = [] + self.updates = "" + self.daemon = daemon + self.dump = crashdump + self.selected_reporters = [] + #FIXME: cache settings! Create some class to represent it like PluginList + self.settings = daemon.getSettings() + pluginlist = getPluginInfoList(daemon) + self.reporters = [] + AnalyzerActionsAndReporters = self.settings["AnalyzerActionsAndReporters"] + for reporter_name in AnalyzerActionsAndReporters[crashdump.getAnalyzerName()].split(','): + reporter = pluginlist.getReporterByName(reporter_name) + if reporter: + self.reporters.append(reporter) + + builderfile = "%s/report.glade" % sys.path[0] + self.builder = gtk.Builder() + self.builder.add_from_file(builderfile) + self.window = self.builder.get_object("w_reporters") + if parent: + self.window.set_transient_for(parent) + self.window.set_modal(True) + self.connect_signal(self.window, "delete-event", self.on_window_delete) + self.connect_signal(self.window, "destroy-event", self.on_window_delete) + + self.pBarWindow = self.builder.get_object("pBarWindow") + + b_cancel = self.builder.get_object("b_close") + self.connect_signal(b_cancel, "clicked", self.on_close_clicked) + reporters_vbox = self.builder.get_object("vb_reporters") + for reporter in self.reporters: + button = gtk.Button(str(reporter)) + self.connect_signal(button, "clicked", self.on_reporter_clicked, data=reporter) + reporters_vbox.pack_start(button) + + # progress bar window to show while bt is being extracted + self.pBarWindow = self.builder.get_object("pBarWindow") + if self.pBarWindow: + self.connect_signal(self.pBarWindow, "delete_event", self.sw_delete_event_cb) + if parent: + self.pBarWindow.set_transient_for(parent) + else: + self.pBarWindow.set_transient_for(self.window) + self.pBar = self.builder.get_object("pBar") + + # connect handlers for daemon signals + #self.ccdaemon.connect("abrt-error", self.error_cb) + self.connect_signal(daemon, "update", self.update_cb) + # for now, just treat them the same (w/o this, we don't even see daemon warnings in logs!): + #self.ccdaemon.connect("warning", self.update_cb) + #self.ccdaemon.connect("show", self.show_cb) + #self.ccdaemon.connect("daemon-state-changed", self.on_daemon_state_changed_cb) + self.connect_signal(daemon, "report-done", self.on_report_done_cb) + self.connect_signal(daemon, "analyze-complete", self.on_analyze_complete_cb, self.pBarWindow) + + def connect_signal(self, obj, signal, callback, data=None): + if data: + signal_id = obj.connect(signal, callback, data) + else: + signal_id = obj.connect(signal, callback) + self.connected_signals.append((obj, signal_id)) + + def disconnect_signals(self): + # we need to disconnect all signals in order to break all references + # to this object, otherwise python won't destroy this object and the + # signals emmited by daemon will get caught by multiple instances of + # this class + for obj, signal_id in self.connected_signals: + obj.disconnect(signal_id) + + def update_cb(self, daemon, message): + self.updates += message + if self.updates[-1] != '\n': + self.updates += '\n' + message = message.replace('\n',' ') + self.builder.get_object("lStatus").set_text(message) + buff = gtk.TextBuffer() + buff.set_text(self.updates) + end = buff.get_insert() + tvUpdates = self.builder.get_object("tvUpdates") + tvUpdates.set_buffer(buff) + tvUpdates.scroll_mark_onscreen(end) + + def sw_delete_event_cb(self, widget, event, data=None): + if self.timer: + gobject.source_remove(self.timer) + widget.hide() + return True + + def show(self): + if not self.reporters: + gui_error_message(_("No reporter plugin available for this type of crash\n" + "Please check abrt.conf.")) + elif len(self.reporters) > 1: + self.builder.get_object("vb_reporters").show_all() + self.window.show() + else: + # we have only one reporter in the list + self.selected_reporters = [str(self.reporters[0])] + self.show_report() + + def on_reporter_clicked(self, widget, reporter): + self.selected_reporters = [str(reporter)] + self.show_report() + + def on_close_clicked(self, widget): + self.disconnect_signals() + self.window.destroy() + + def on_window_delete(self, window, event): + self.disconnect_signals() + return False + + def on_report_done_cb(self, daemon, result): + try: + gobject.source_remove(self.timer) + except: + pass + self.pBarWindow.hide() + gui_report_dialog(result, self.window) + + if not self.window.get_property("visible"): + self.disconnect_signals() + + def on_analyze_complete_cb(self, daemon, report, pBarWindow): + try: + gobject.source_remove(self.timer) + except: + pass + self.pBarWindow.hide() +#FIXME - why we need this?? -> timeout warnings +# try: +# dumplist = getDumpList(self.daemon) +# except Exception, e: +# print e + if not report: + gui_error_message(_("Unable to get report!\nDebuginfo is missing?")) + return + + # if we have only one reporter enabled, the window with + # the selection is not shown, so we can't use it as a parent + # and we use the mainwindow instead + if self.window.get_property("visible"): + parent_window = self.window + else: + parent_window = self.window.get_transient_for() + + report_dialog = ReporterDialog(report, self.daemon, log=self.updates, parent=parent_window) + # (response, report) + response, result = report_dialog.run() + + if response == gtk.RESPONSE_APPLY: + try: + self.pBarWindow.show_all() + self.timer = gobject.timeout_add(100, self.progress_update_cb) + pluginlist = getPluginInfoList(self.daemon) + reporters_settings = pluginlist.getReporterPluginsSettings() + log2("Report(result,reporters,settings):") + log2(" result:%s", str(result)) + # Careful, this will print reporters_settings["Password"] too + log2(" settings:%s", str(reporters_settings)) + self.daemon.Report(result, self.selected_reporters, reporters_settings) + log2("Report() returned") + #self.hydrate() + except Exception, ex: + gui_error_message(_("Reporting failed!\n%s" % ex)) + # -50 == REFRESH + elif response == -50: + self.refresh_report(report) + + elif not self.window.get_property("visible"): + self.disconnect_signals() + + # call to update the progressbar + def progress_update_cb(self, *args): + self.pBar.pulse() + return True + + def refresh_report(self, report): + self.updates = "" + self.pBarWindow.show_all() + self.timer = gobject.timeout_add(100, self.progress_update_cb) + + # show the report window with selected report + try: + self.daemon.getReport(report[CD_UUID][CD_CONTENT], force=1) + except Exception, ex: + # 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 + if self.timer: + gobject.source_remove(self.timer) + self.pBarWindow.hide() + gui_error_message(_("Error getting the report: %s" % ex)) + return + + def show_report(self): + self.updates = "" + # FIXME don't duplicate the code, move to function + #self.pBar.show() + self.pBarWindow.show_all() + self.timer = gobject.timeout_add(100, self.progress_update_cb) + + # show the report window with selected dump + # when getReport is done it emits "analyze-complete" and on_analyze_complete_cb is called + # FIXME: does it make sense to change it to use callback rather then signal emitting? + try: + self.daemon.getReport(self.dump.getUUID()) + except Exception, ex: + # 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 + if self.timer: + gobject.source_remove(self.timer) + self.pBarWindow.hide() + gui_error_message(_("Error getting the report: %s" % ex)) + return + + def __del__(self): + log1("ReporterSelector: instance is about to be garbage-collected") diff --git a/src/Gui/PluginList.py b/src/Gui/PluginList.py index 6105b04e..288dfccf 100644 --- a/src/Gui/PluginList.py +++ b/src/Gui/PluginList.py @@ -24,6 +24,13 @@ class PluginInfoList(list): else: log("PluginInfoList: db == None") + def getReporterByName(self, name): + try: + return [x for x in self if x["Name"] == name][0] + except: + # if such reporter doesnt't exist return None + return None + def getEnabledPlugins(self): return [x for x in self if x["Enabled"] == 'yes'] diff --git a/src/Gui/ccgui.glade b/src/Gui/ccgui.glade index 2291ee6f..5b102f46 100644 --- a/src/Gui/ccgui.glade +++ b/src/Gui/ccgui.glade @@ -2,76 +2,6 @@ - - 270 - 12 - Please wait.. - True - center-on-parent - 470 - abrt - main_window3 - - - True - vertical - 12 - - - True - 0 - - - False - 0 - - - - - True - 0 - - - False - 1 - - - - - True - True - - - True - True - automatic - automatic - etched-in - - - True - True - - - - - - - True - Details - - - label_item - - - - - 2 - - - - - 5 About ABRT diff --git a/src/Gui/report.glade b/src/Gui/report.glade index 7b3aac4a..0213026b 100644 --- a/src/Gui/report.glade +++ b/src/Gui/report.glade @@ -5,6 +5,8 @@ 5 Automatic Bug Reporting Tool + True + abrt normal False @@ -649,4 +651,138 @@ bSend + + Reporter Selector + abrt + + + True + vertical + + + True + 5 + 10 + <b>Where do you want to report this incident?</b> + True + + + 0 + + + + + True + vertical + + + + + + 1 + + + + + True + + + + + + 9 + 2 + + + + + True + + + gtk-close + True + True + True + True + + + 0 + + + + + False + 3 + + + + + + + 270 + 12 + Please wait.. + True + center-on-parent + 470 + abrt + w_reporters + + + True + vertical + 12 + + + True + 0 + + + False + 0 + + + + + True + 0 + + + False + 1 + + + + + True + True + + + True + True + automatic + automatic + etched-in + + + True + True + + + + + + + True + Details + + + + + 2 + + + + + -- cgit