Use a custom QDialog for error/warning/ingo/question dialogs. This allows for clickable links in the dialog messages

This commit is contained in:
Kovid Goyal 2011-01-26 19:48:02 -07:00
parent a16f4a4b2c
commit a290e0f1ba
3 changed files with 229 additions and 84 deletions

View File

@ -8,12 +8,12 @@ from urllib import unquote
from PyQt4.Qt import QVariant, QFileInfo, QObject, SIGNAL, QBuffer, Qt, \ from PyQt4.Qt import QVariant, QFileInfo, QObject, SIGNAL, QBuffer, Qt, \
QByteArray, QTranslator, QCoreApplication, QThread, \ QByteArray, QTranslator, QCoreApplication, QThread, \
QEvent, QTimer, pyqtSignal, QDate, QDesktopServices, \ QEvent, QTimer, pyqtSignal, QDate, QDesktopServices, \
QFileDialog, QMessageBox, QPixmap, QFileIconProvider, \ QFileDialog, QFileIconProvider, \
QIcon, QApplication, QDialog, QPushButton, QUrl, QFont QIcon, QApplication, QDialog, QUrl, QFont
ORG_NAME = 'KovidsBrain' ORG_NAME = 'KovidsBrain'
APP_UID = 'libprs500' APP_UID = 'libprs500'
from calibre.constants import islinux, iswindows, isosx, isfreebsd, isfrozen from calibre.constants import islinux, iswindows, isfreebsd, isfrozen
from calibre.utils.config import Config, ConfigProxy, dynamic, JSONConfig from calibre.utils.config import Config, ConfigProxy, dynamic, JSONConfig
from calibre.utils.localization import set_qt_translator from calibre.utils.localization import set_qt_translator
from calibre.ebooks.metadata.meta import get_metadata, metadata_from_formats from calibre.ebooks.metadata.meta import get_metadata, metadata_from_formats
@ -178,104 +178,40 @@ def is_widescreen():
def extension(path): def extension(path):
return os.path.splitext(path)[1][1:].lower() return os.path.splitext(path)[1][1:].lower()
class CopyButton(QPushButton):
ACTION_KEYS = [Qt.Key_Enter, Qt.Key_Return, Qt.Key_Space]
def copied(self):
self.emit(SIGNAL('copy()'))
self.setDisabled(True)
self.setText(_('Copied'))
def keyPressEvent(self, ev):
try:
if ev.key() in self.ACTION_KEYS:
self.copied()
return
except:
pass
QPushButton.keyPressEvent(self, ev)
def keyReleaseEvent(self, ev):
try:
if ev.key() in self.ACTION_KEYS:
return
except:
pass
QPushButton.keyReleaseEvent(self, ev)
def mouseReleaseEvent(self, ev):
ev.accept()
self.copied()
class MessageBox(QMessageBox):
def __init__(self, type_, title, msg, buttons, parent, det_msg=''):
QMessageBox.__init__(self, type_, title, msg, buttons, parent)
self.title = title
self.msg = msg
self.det_msg = det_msg
self.setDetailedText(det_msg)
# Cannot set keyboard shortcut as the event is not easy to filter
self.cb = CopyButton(_('Copy') if isosx else _('Copy to Clipboard'))
self.connect(self.cb, SIGNAL('copy()'), self.copy_to_clipboard)
self.addButton(self.cb, QMessageBox.ActionRole)
default_button = self.button(self.Ok)
if default_button is None:
default_button = self.button(self.Yes)
if default_button is not None:
self.setDefaultButton(default_button)
def copy_to_clipboard(self):
QApplication.clipboard().setText('%s: %s\n\n%s' %
(self.title, self.msg, self.det_msg))
def warning_dialog(parent, title, msg, det_msg='', show=False, def warning_dialog(parent, title, msg, det_msg='', show=False,
show_copy_button=True): show_copy_button=True):
d = MessageBox(QMessageBox.Warning, 'WARNING: '+title, msg, QMessageBox.Ok, from calibre.gui2.dialogs.message_box import MessageBox
parent, det_msg) d = MessageBox(MessageBox.WARNING, 'WARNING: '+title, msg, det_msg, parent=parent,
d.setEscapeButton(QMessageBox.Ok) show_copy_button=show_copy_button)
d.setIconPixmap(QPixmap(I('dialog_warning.png')))
if not show_copy_button:
d.cb.setVisible(False)
if show: if show:
return d.exec_() return d.exec_()
return d return d
def error_dialog(parent, title, msg, det_msg='', show=False, def error_dialog(parent, title, msg, det_msg='', show=False,
show_copy_button=True): show_copy_button=True):
d = MessageBox(QMessageBox.Critical, 'ERROR: '+title, msg, QMessageBox.Ok, from calibre.gui2.dialogs.message_box import MessageBox
parent, det_msg) d = MessageBox(MessageBox.ERROR, 'ERROR: '+title, msg, det_msg, parent=parent,
d.setIconPixmap(QPixmap(I('dialog_error.png'))) show_copy_button=show_copy_button)
d.setEscapeButton(QMessageBox.Ok)
if not show_copy_button:
d.cb.setVisible(False)
if show: if show:
return d.exec_() return d.exec_()
return d return d
def question_dialog(parent, title, msg, det_msg='', show_copy_button=True, def question_dialog(parent, title, msg, det_msg='', show_copy_button=False,
buttons=QMessageBox.Yes|QMessageBox.No, yes_button=QMessageBox.Yes): buttons=None, yes_button=None):
d = MessageBox(QMessageBox.Question, title, msg, buttons, from calibre.gui2.dialogs.message_box import MessageBox
parent, det_msg) d = MessageBox(MessageBox.QUESTION, title, msg, det_msg, parent=parent,
d.setIconPixmap(QPixmap(I('dialog_question.png'))) show_copy_button=show_copy_button)
d.setEscapeButton(QMessageBox.No) if buttons is not None:
if not show_copy_button: d.bb.setStandardButtons(buttons)
d.cb.setVisible(False)
return d.exec_() == yes_button return d.exec_() == d.Accepted
def info_dialog(parent, title, msg, det_msg='', show=False, def info_dialog(parent, title, msg, det_msg='', show=False,
show_copy_button=True): show_copy_button=True):
d = MessageBox(QMessageBox.Information, title, msg, QMessageBox.Ok, from calibre.gui2.dialogs.message_box import MessageBox
parent, det_msg) d = MessageBox(MessageBox.INFO, title, msg, det_msg, parent=parent,
d.setIconPixmap(QPixmap(I('dialog_information.png'))) show_copy_button=show_copy_button)
if not show_copy_button:
d.cb.setVisible(False)
if show: if show:
return d.exec_() return d.exec_()

View File

@ -0,0 +1,104 @@
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
__license__ = 'GPL v3'
__copyright__ = '2011, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
from PyQt4.Qt import QDialog, QIcon, QApplication, QSize, QKeySequence, \
QAction, Qt
from calibre.constants import __version__
from calibre.gui2.dialogs.message_box_ui import Ui_Dialog
class MessageBox(QDialog, Ui_Dialog):
ERROR = 0
WARNING = 1
INFO = 2
QUESTION = 3
def __init__(self, type_, title, msg, det_msg='', show_copy_button=True,
parent=None):
QDialog.__init__(self, parent)
icon = {
self.ERROR : 'error',
self.WARNING: 'warning',
self.INFO: 'information',
self.QUESTION: 'question',
}[type_]
icon = 'dialog_%s.png'%icon
self.icon = QIcon(I(icon))
self.setupUi(self)
self.setWindowTitle(title)
self.setWindowIcon(self.icon)
self.icon_label.setPixmap(self.icon.pixmap(128, 128))
self.msg.setText(msg)
self.det_msg.setPlainText(det_msg)
self.det_msg.setVisible(False)
if det_msg:
self.show_det_msg = _('Show &details')
self.hide_det_msg = _('Hide &details')
self.det_msg_toggle = self.bb.addButton(self.show_det_msg, self.bb.ActionRole)
self.det_msg_toggle.clicked.connect(self.toggle_det_msg)
self.det_msg_toggle.setToolTip(
_('Show detailed information about this error'))
if show_copy_button:
self.ctc_button = self.bb.addButton(_('&Copy to clipboard'),
self.bb.ActionRole)
self.ctc_button.clicked.connect(self.copy_to_clipboard)
self.copy_action = QAction(self)
self.addAction(self.copy_action)
self.copy_action.setShortcuts(QKeySequence.Copy)
self.copy_action.triggered.connect(self.copy_to_clipboard)
self.is_question = type_ == self.QUESTION
if self.is_question:
self.bb.setStandardButtons(self.bb.Yes|self.bb.No)
self.bb.button(self.bb.Yes).setDefault(True)
else:
self.bb.button(self.bb.Ok).setDefault(True)
self.do_resize()
def toggle_det_msg(self, *args):
vis = self.det_msg.isVisible()
self.det_msg_toggle.setText(self.show_det_msg if vis else
self.hide_det_msg)
self.det_msg.setVisible(not vis)
self.do_resize()
def do_resize(self):
sz = self.sizeHint() + QSize(100, 0)
sz.setWidth(min(500, sz.width()))
sz.setHeight(min(500, sz.height()))
self.resize(sz)
def copy_to_clipboard(self, *args):
QApplication.clipboard().setText(
'calibre, version %s\n%s: %s\n\n%s' %
(__version__, unicode(self.windowTitle()),
unicode(self.msg.text()),
unicode(self.det_msg.toPlainText())))
self.ctc_button.setText(_('Copied'))
def showEvent(self, ev):
ret = QDialog.showEvent(self, ev)
if self.is_question:
self.bb.button(self.bb.Yes).setFocus(Qt.OtherFocusReason)
else:
self.bb.button(self.bb.Ok).setFocus(Qt.OtherFocusReason)
return ret
if __name__ == '__main__':
app = QApplication([])
from calibre.gui2 import question_dialog
print question_dialog(None, 'title', 'msg <a href="http://google.com">goog</a> ',
det_msg='det '*1000,
show_copy_button=True)

View File

@ -0,0 +1,105 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>497</width>
<height>235</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="icon_label">
<property name="maximumSize">
<size>
<width>68</width>
<height>68</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="../../../../resources/images.qrc">:/images/dialog_warning.png</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="msg">
<property name="text">
<string/>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QPlainTextEdit" name="det_msg">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QDialogButtonBox" name="bb">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="../../../../resources/images.qrc"/>
</resources>
<connections>
<connection>
<sender>bb</sender>
<signal>accepted()</signal>
<receiver>Dialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>bb</sender>
<signal>rejected()</signal>
<receiver>Dialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>