3
''' KDE 4 Apport User Interface
5
Copyright (C) 2007-2009 Canonical Ltd.
6
Author: Richard A. Johnson <nixternal@ubuntu.com>
8
This program is free software; you can redistribute it and/or modify it
9
under the terms of the GNU General Public License as published by the
10
Free Software Foundation; either version 2 of the License, or (at your
11
option) any later version. See http://www.gnu.org/copyleft/gpl.html for
12
the full text of the license.
20
from PyQt4.QtCore import *
21
from PyQt4.QtGui import (QDialog, QLabel, QCheckBox, QRadioButton,
22
QTreeWidget, QTreeWidgetItem, QMessageBox,
23
QVBoxLayout, QFileDialog, QDialogButtonBox,
24
QProgressBar, QGroupBox)
26
from PyKDE4.kdecore import (i18n, ki18n, KAboutData, KCmdLineArgs,
28
from PyKDE4.kdeui import (KApplication, KNotification, KMessageBox, KIcon,
31
except ImportError, e:
32
# this can happen while upgrading python packages
33
print >> sys.stderr, 'Could not import module, is a package upgrade in progress? Error:', e
36
def translate(self, prop):
37
''' Reimplement method from uic to change it to use gettext '''
38
if prop.get("notr", None) == "true":
39
return self._cstring(prop)
43
text = prop.text.encode("UTF-8")
46
uic.properties.Properties._string = translate
48
class Dialog(QDialog):
49
''' Main dialog wrapper '''
51
def __init__(self, ui, title, heading, text):
52
QDialog.__init__(self, None, Qt.Window)
54
uic.loadUi(os.path.join(os.path.dirname(sys.argv[0]), ui), self)
56
self.setWindowTitle(title)
57
if self.findChild(QLabel, 'heading'):
58
self.findChild(QLabel, 'heading').setText('<h2>%s</h2>' % heading)
59
self.findChild(QLabel, 'text').setText(text)
61
def on_buttons_clicked(self, button):
62
self.actionbutton = button
63
if self.sender().buttonRole(button) == QDialogButtonBox.ActionRole:
64
button.window().done(2)
66
def addbutton(self, button):
67
return self.findChild(QDialogButtonBox, 'buttons').addButton(button,
68
QDialogButtonBox.ActionRole)
70
class ErrorDialog(Dialog):
71
''' Error dialog wrapper '''
73
def __init__(self, title, heading, text, checker=None):
74
Dialog.__init__(self, 'error.ui', title, heading, text)
76
self.setMaximumSize(1, 1)
77
self.findChild(QLabel, 'icon').setPixmap(
78
QMessageBox.standardIcon(QMessageBox.Critical))
80
self.checker = self.findChild(QCheckBox, 'checker')
82
self.checker.setText(checker)
87
return self.checker.isChecked()
89
class ChoicesDialog(Dialog):
90
''' Choices dialog wrapper '''
92
def __init__(self, title, text):
93
Dialog.__init__(self, 'choices.ui', title, None, text)
95
self.setMaximumSize(1, 1)
97
def on_buttons_clicked(self, button):
98
Dialog.on_buttons_clicked(self, button)
99
if self.sender().buttonRole(button) == QDialogButtonBox.RejectRole:
102
class ProgressDialog(Dialog):
103
''' Progress dialog wrapper '''
105
def __init__(self, title, heading, text):
106
Dialog.__init__(self, 'progress.ui', title, heading, text)
108
self.setMaximumSize(1, 1)
110
def on_buttons_clicked(self, button):
111
Dialog.on_buttons_clicked(self, button)
112
if self.sender().buttonRole(button) == QDialogButtonBox.RejectRole:
115
def set(self, value=None):
116
progress = self.findChild(QProgressBar, 'progress')
118
progress.setRange(0, 0)
121
progress.setRange(0, 1000)
122
progress.setValue(value * 1000)
124
class ReportDialog(Dialog):
125
''' Report dialog wrapper '''
127
def __init__(self, title, heading, text):
128
Dialog.__init__(self, 'bugreport.ui', title, heading, text)
130
self.details = self.addbutton(i18n("&Details..."))
131
self.details.setCheckable(True)
133
self.treeview = self.findChild(QTreeWidget, 'details')
136
def on_buttons_clicked(self, button):
137
if self.details == button:
138
self.showtree(button.isChecked())
140
Dialog.on_buttons_clicked(self, button)
141
if self.sender().buttonRole(button) == QDialogButtonBox.RejectRole:
144
def showtree(self, visible):
145
self.treeview.setVisible(visible)
147
self.setMaximumSize(16777215, 16777215)
149
self.setMaximumSize(1, 1)
151
class MainUserInterface(apport.ui.UserInterface):
152
''' The main user interface presented to the user '''
155
apport.ui.UserInterface.__init__(self)
160
# ui_* implementation of abstract UserInterface classes
163
def ui_present_crash(self, desktop_entry):
164
# adapt dialog heading and label appropriately
166
name = desktop_entry.getName()
167
heading = i18n('Sorry, %s closed unexpectedly') % name
168
elif self.report.has_key('ExecutablePath'):
169
name = os.path.basename(self.report['ExecutablePath'])
170
heading = i18n('Sorry, the program "%s" closed unexpectedly.') % name
172
name = self.cur_package
173
heading = i18n('Sorry, %s closed unexpectedly.') % name
175
dialog = ErrorDialog(name, heading,
176
i18n('If you were not doing anything confidential (entering '
177
'passwords or other private information), you can help '
178
'to improve the application by reporting the problem.'),
179
i18n('&Ignore future crashes of this program version'))
181
reportbutton = dialog.addbutton(i18n('&Report Problem...'))
183
if desktop_entry and self.report.has_key('ExecutablePath') and \
184
os.path.dirname(self.report['ExecutablePath']) in \
185
os.environ['PATH'].split(':') and subprocess.call(['pgrep',
186
'-x', os.path.basename(self.report['ExecutablePath']),
187
'-u', str(os.getuid())], stdout=subprocess.PIPE) != 0:
188
restartbutton = dialog.addbutton(i18n('Restart &Program'))
190
# show crash notification dialog
191
response = dialog.exec_()
192
blacklist = dialog.checked()
194
if response == QDialog.Rejected:
195
return {'action': 'cancel', 'blacklist': blacklist}
196
if dialog.actionbutton == reportbutton:
197
return {'action': 'report', 'blacklist': blacklist}
198
if dialog.actionbutton == restartbutton:
199
return {'action': 'restart', 'blacklist': blacklist}
201
return {'action': 'cancel', 'blacklist': blacklist}
203
def ui_present_package_error(self):
204
name = self.report['Package']
205
dialog = ErrorDialog(name,
206
i18n('Sorry, the package "%s" failed to install or upgrade.') %
208
i18n('You can help the developers to fix the package by '
209
'reporting the problem.'))
211
reportbutton = dialog.addbutton(i18n('&Report Problem...'))
213
response = dialog.exec_()
215
if response == QDialog.Rejected:
217
if dialog.actionbutton == reportbutton:
222
def ui_present_kernel_error(self):
223
message = i18n('Your system encountered a serious kernel problem.')
225
if self.report.has_key('Annotation'):
226
annotate = self.report['Annotation'] + '\n\n'
227
annotate += i18n('You can help the developers to fix the problem by '
230
dialog = ErrorDialog(i18n('Kernel problem'), message, annotate)
232
reportbutton = dialog.addbutton(i18n('&Report Problem...'))
234
response = dialog.exec_()
236
if response == QDialog.Rejected:
238
if dialog.actionbutton == reportbutton:
243
def ui_present_report_details(self):
244
dialog = ReportDialog(self.report.get('Package',
245
i18n('Generic error')).split()[0],
246
i18n('Send problem report to the developers?'),
247
i18n('After the problem report has been sent, please fill out '
248
'the form in the automatically opened web browser.'))
250
sendbutton = dialog.addbutton(i18n('&Send'))
251
sendbutton.setDefault(True)
254
details = dialog.findChild(QTreeWidget, 'details')
255
for key in self.report:
256
keyitem = QTreeWidgetItem([key])
257
details.addTopLevelItem(keyitem)
260
if not hasattr(self.report[key], 'gzipvalue') and \
261
hasattr(self.report[key], 'isspace') and \
262
not self.report._is_binary(self.report[key]):
263
lines = self.report[key].splitlines()
265
QTreeWidgetItem(keyitem, [line])
267
keyitem.setExpanded(True)
269
QTreeWidgetItem(keyitem, [i18n('(binary data)')])
271
details.header().hide()
273
# complete/reduce radio buttons
274
if self.report.has_key('CoreDump') and \
275
self.report.has_useful_stacktrace():
276
dialog.findChild(QRadioButton, 'complete').setText(
277
i18n('Complete report (recommended; %s)') %
278
self.format_filesize(self.get_complete_size()))
279
dialog.findChild(QRadioButton, 'reduced').setText(
280
i18n('Reduced report (slow Internet connection; %s)') %
281
self.format_filesize(self.get_reduced_size()))
283
dialog.findChild(QGroupBox, 'options').hide()
285
response = dialog.exec_()
287
if response == QDialog.Rejected:
290
if dialog.actionbutton != sendbutton:
292
if dialog.findChild(QRadioButton, 'reduced').isChecked():
294
if dialog.findChild(QRadioButton, 'complete').isChecked():
299
def ui_info_message(self, title, text):
300
KMessageBox.information(None, i18n(text), i18n(title))
302
def ui_error_message(self, title, text):
303
KMessageBox.information(None, i18n(text), i18n(title))
305
def ui_start_info_collection_progress(self):
306
self.progress = ProgressDialog(
307
i18n('Collecting Problem Information'),
308
i18n('Collecting problem information'),
309
i18n('The collected information can be sent to the developers '
310
'to improve the application. This might take a few '
315
def ui_pulse_info_collection_progress(self):
317
KApplication.processEvents()
319
def ui_stop_info_collection_progress(self):
322
def ui_start_upload_progress(self):
323
self.progress = ProgressDialog(
324
i18n('Uploading Problem Information'),
325
i18n('Uploading problem information'),
326
i18n('The collected information is being sent to the bug '
327
'tracking system. This might take a few minutes.'))
330
def ui_set_upload_progress(self, progress):
332
self.progress.set(progress)
335
KApplication.processEvents()
337
def ui_stop_upload_progress(self):
340
def ui_question_yesno(self, text):
341
response = KMessageBox.questionYesNoCancel(None, i18n(text), QString(),
342
KStandardGuiItem.yes(), KStandardGuiItem.no(),
343
KStandardGuiItem.cancel())
344
if response == KMessageBox.Yes:
346
if response == KMessageBox.No:
350
def ui_question_choice(self, text, options, multiple):
351
''' Show a question with predefined choices.
353
@options is a list of strings to present.
354
@multiple - if True, choices should be QCheckBoxes, if False then
355
should be QRadioButtons.
357
Return list of selected option indexes, or None if the user cancelled.
358
If multiple is False, the list will always have one element.
361
dialog = ChoicesDialog(i18n("Apport"), text)
364
for option in options:
366
b = QCheckBox(option)
368
b = QRadioButton(option)
369
dialog.vbox_choices.insertWidget(0, b)
371
response = dialog.exec_()
373
if response == QDialog.Rejected:
376
response = [c for c in range(0, dialog.vbox_choices.count()) if \
377
dialog.vbox_choices.itemAt(c).widget().isChecked()]
381
def ui_question_file(self, text):
382
''' Show a file selector dialog.
384
Return path if the user selected a file, or None if cancelled.
387
response = QFileDialog.getOpenFileName(None, unicode(text, 'UTF-8'))
388
if response.length() == 0:
392
if __name__ == '__main__':
393
appName = "apport-kde"
394
catalog = "apport-kde"
395
programName = ki18n("Apport KDE")
397
description = ki18n("KDE 4 frontend for the apport crash report system")
398
license = KAboutData.License_GPL
399
copyright = ki18n("2009 Canonical Ltd.")
400
text = KLocalizedString()
401
homePage = "https://wiki.ubuntu.com/AutomatedProblemReports"
402
bugEmail = "kubuntu-devel@lists.ubuntu.com"
404
aboutData = KAboutData(appName, catalog, programName, version, description,
405
license, copyright, text, homePage, bugEmail)
407
aboutData.addAuthor(ki18n("Richard A. Johnson"), ki18n("Author"))
408
aboutData.addAuthor(ki18n("Michael Hofmann"), ki18n("Original Qt4 Author"))
410
KCmdLineArgs.init([""], aboutData)
413
app.setWindowIcon(KIcon("apport"))
415
UserInterface = MainUserInterface()