home *** CD-ROM | disk | FTP | other *** search
/ PC Welt 2006 November (DVD) / PCWELT_11_2006.ISO / casper / filesystem.squashfs / usr / share / apport / apport-gtk < prev    next >
Encoding:
Text File  |  2006-08-25  |  7.2 KB  |  238 lines

  1. #!/usr/bin/python
  2. import gobject, gtk, gtk.glade
  3. import glob, sys, gettext, os.path, locale, tempfile
  4. from gettext import gettext as _
  5. import webbrowser, subprocess
  6. import xdg.DesktopEntry
  7.  
  8. from problem_report import ProblemReport
  9. import apport_utils
  10.  
  11. gettext_domain = 'apport'
  12.  
  13. def format_filesize(size):
  14.     '''Format the given integer as humanly readable and i18n'ed file size.'''
  15.  
  16.     if size < 1048576:
  17.         return locale.format('%.1f KB', size/1024.)
  18.     if size < 1024 * 1048576:
  19.         return locale.format('%.1f MB', size / 1048576.)
  20.     return locale.format('%.1f GB', size / float(1024 * 1048576)) 
  21.  
  22. class ApportGTK:
  23.     def __init__(self):
  24.     self.widgets = gtk.glade.XML(os.path.join(os.path.dirname(sys.argv[0]),
  25.         'apport-gtk.glade'))
  26.         self.widgets.signal_autoconnect(self)
  27.  
  28.     self.reports = apport_utils.get_new_reports()
  29.     if len(self.reports) == 0:
  30.         print >> sys.stderr, 'No crash reports found, aborting'
  31.         sys.exit(1)
  32.  
  33.     # initialize tree model and view
  34.     self.tree_model = gtk.TreeStore(gobject.TYPE_STRING)
  35.     self.w('treeview_reportdetails').set_model(self.tree_model)
  36.  
  37.     column = gtk.TreeViewColumn("Report", gtk.CellRendererText(), text=0)
  38.     self.w('treeview_reportdetails').append_column(column)
  39.  
  40.     # save the original strings of the dialog headings (which contain %s,
  41.     # which we replace later)
  42.     self.str_heading = self.w('label_heading').get_label()
  43.     self.str_heading_reopen = self.w('label_heading_reopen').get_label()
  44.     self.str_bugreport_title = self.w('window_bugreport').get_title()
  45.  
  46.     self.cur_source = None
  47.     self.report = None
  48.     self.update_report()
  49.  
  50.     def ignore(self, widget, *auxargs):
  51.     '''Do nothing with the current report.'''
  52.  
  53.     self.w('dialog_crash').hide()
  54.     self.w('dialog_crash_reopen').hide()
  55.     self.next()
  56.     return True
  57.  
  58.     def next(self):
  59.     '''Move on to the next report.'''
  60.  
  61.     apport_utils.mark_report_seen(self.reports.pop(0))
  62.     self.report = None
  63.  
  64.     if len(self.reports) > 0:
  65.         self.update_report()
  66.     else:
  67.         gtk.main_quit()
  68.  
  69.     def w(self, widget):
  70.     '''Shortcut for getting a widget.'''
  71.  
  72.     return self.widgets.get_widget(widget)
  73.  
  74.     def update_report(self):
  75.     '''Load currently selected report and update UI accordingly.'''
  76.  
  77.     # load problem report
  78.     self.report = ProblemReport()
  79.     self.report.load(open(self.reports[0]), binary=False)
  80.  
  81.     subject = self.report.get('ExecutablePath', None)
  82.     if not subject:
  83.         subject = self.report['Package'].split()[0]
  84.  
  85.     # create reduced report if there are any removed fields
  86.     if self.report.has_removed_fields():
  87.         (fd, self.reduced_file) = tempfile.mkstemp('.crash', 
  88.         subject.replace('/', '_') + '.')
  89.         os.close(fd)
  90.         self.report.write(open(self.reduced_file, 'w'))
  91.     else:
  92.         self.reduced_file = None
  93.  
  94.     package = self.report['Package'].split()[0]
  95.     try:
  96.         self.cur_source = self.report['SourcePackage']
  97.     except KeyError:
  98.         self.cur_source = package
  99.  
  100.     # try to get a matching .desktop file
  101.     if self.report.has_key('DesktopFile') and os.path.exists(self.report['DesktopFile']):
  102.         desktop_file = self.report['DesktopFile']
  103.     else:
  104.         desktop_file = apport_utils.find_package_desktopfile(package)
  105.     self.desktop_entry = None
  106.     if desktop_file:
  107.         try:
  108.         self.desktop_entry = xdg.DesktopEntry.DesktopEntry(desktop_file)
  109.         except: pass
  110.  
  111.     # update dialog
  112.     if self.desktop_entry:
  113.         heading = _('Sorry, %s closed unexpectedly.') % self.desktop_entry.getName()
  114.     elif self.report.has_key('ExecutablePath'):
  115.         heading = _('Sorry, the program "%s" closed unexpectedly.') % os.path.basename(self.report['ExecutablePath'])
  116.     else:
  117.         heading = _('Sorry, %s closed unexpectedly.') % self.report['Package'].split()[0]
  118.     if self.desktop_entry and self.report.has_key('ExecutablePath') and \
  119.         subprocess.call(['pidof', '-x', self.report['ExecutablePath']], stdout=subprocess.PIPE) != 0:
  120.         label = self.w('label_heading_reopen')
  121.         label.set_markup(self.str_heading % heading)
  122.         self.w('dialog_crash_reopen').show_all()
  123.     else:
  124.         label = self.w('label_heading')
  125.         label.set_markup(self.str_heading % heading)
  126.         self.w('dialog_crash').show_all()
  127.  
  128.     def on_button_reportbug_clicked(self, widget):
  129.     '''Open bug page in browser and ask to file a bug.'''
  130.  
  131.     assert self.report
  132.  
  133.     self.w('dialog_crash').hide()
  134.     self.w('dialog_crash_reopen').hide()
  135.     
  136.     mode = self.report.get('BugDisplayMode', 'list')
  137.  
  138.     if mode == 'file':
  139.         url = 'https://launchpad.net/distros/ubuntu/+source/%s/+filebug' % self.cur_source
  140.         self.w('label_bug_instructions').set_text(_('Please fill out the bug \
  141. report form in your web browser and give some details about what you did just \
  142. before the crash.'))
  143.     else:
  144.         url = 'https://launchpad.net/distros/ubuntu/+source/%s/+bugs' % self.cur_source
  145.         self.w('label_bug_instructions').set_text(_('Please check the list \
  146. in your web browser for an already existing bug report about this problem. If \
  147. there is none, or you are in doubt, please create a new report.'))
  148.  
  149.     # update details
  150.     self.w('window_bugreport').set_title(self.str_bugreport_title %
  151.         self.report['Package'].split()[0])
  152.  
  153.     self.w('label_reportfile').set_selectable(True)
  154.     self.update_reportfile_size()
  155.  
  156.     self.tree_model.clear()
  157.     row = 0
  158.     for key in self.report:
  159.         keyiter = self.tree_model.insert_before(None, None)
  160.         self.tree_model.set_value(keyiter, 0, key)
  161.  
  162.         valiter = self.tree_model.insert_before(keyiter, None)
  163.         if self.report[key]:
  164.         self.tree_model.set_value(valiter, 0, self.report[key])
  165.         else:
  166.         self.tree_model.set_value(valiter, 0, '(binary data)')
  167.  
  168.         # expand the row if the value has less than 5 lines
  169.         if len(filter(lambda c: c == '\n', self.report[key])) < 4:
  170.         self.w('treeview_reportdetails').expand_row(row, False)
  171.         row += 1
  172.  
  173.     if self.reduced_file:
  174.         self.w('radiobutton_complete').show()
  175.         self.w('radiobutton_reduced').show()
  176.     else:
  177.         self.w('radiobutton_complete').hide()
  178.         self.w('radiobutton_reduced').hide()
  179.  
  180.     self.w('window_bugreport').show()
  181.  
  182.     # prefer gnome-open if it exists
  183.     try:
  184.         if subprocess.call(['gnome-open', url]) == 0:
  185.         return
  186.     except OSError:
  187.         pass
  188.  
  189.     webbrowser.open(url)
  190.     return True
  191.  
  192.     def on_window_bugreport_delete_event(self, widget, event):
  193.     self.w('window_bugreport').hide()
  194.     if self.reduced_file:
  195.         os.unlink(self.reduced_file)
  196.     self.next()
  197.     return True
  198.  
  199.     def on_button_reopen_clicked(self, widget):
  200.     '''Reopen the crashed application.'''
  201.  
  202.     self.w('dialog_crash').hide()
  203.     self.w('dialog_crash_reopen').hide()
  204.  
  205.     assert self.report.has_key('ProcCmdline')
  206.  
  207.     if os.fork() == 0:
  208.         os.setsid()
  209.         os.execlp('sh', 'sh', '-c', self.report.get('RespawnCommand', self.report['ProcCmdline']))
  210.         sys.exit(1)
  211.  
  212.     self.next()
  213.     return True
  214.  
  215.     def on_expander_details_activate(self, widget):
  216.     # signal is sent before actually expanding/collapsing, thus this
  217.     # requires negation
  218.     self.w('window_bugreport').set_resizable(not self.w('expander_details').get_expanded())
  219.  
  220.     def update_reportfile_size(self):
  221.     if not self.reduced_file or self.w('radiobutton_complete').get_active():
  222.         f = self.reports[0]
  223.     else:
  224.         f = self.reduced_file
  225.     self.w('label_reportfile').set_text(f)
  226.     self.w('label_reportsize').set_text('(%s)' % 
  227.         format_filesize(os.path.getsize(f)))
  228.  
  229.     def on_radiobutton_complete_toggled(self, widget):
  230.     self.update_reportfile_size()
  231.  
  232. if __name__ == '__main__':
  233.     gettext.textdomain(gettext_domain)
  234.     gtk.glade.textdomain(gettext_domain)
  235.     app = ApportGTK()
  236.     gtk.main()
  237.  
  238.