diff --git a/resources/recipes/ibm_smarter_planet.recipe b/resources/recipes/ibm_smarter_planet.recipe
index 44978142f6..be26b29fc6 100644
--- a/resources/recipes/ibm_smarter_planet.recipe
+++ b/resources/recipes/ibm_smarter_planet.recipe
@@ -1,17 +1,18 @@
+
from calibre.web.feeds.news import BasicNewsRecipe
class AdvancedUserRecipe1293122276(BasicNewsRecipe):
- title = u'Smarter Planet | Tumblr for eReaders'
+ title = u'Smarter Planet | Tumblr'
__author__ = 'Jack Mason'
author = 'IBM Global Business Services'
publisher = 'IBM'
language = 'en'
category = 'news, technology, IT, internet of things, analytics'
- oldest_article = 7
+ oldest_article = 14
max_articles_per_feed = 30
no_stylesheets = True
use_embedded_content = False
- masthead_url = 'http://30.media.tumblr.com/tumblr_l70dow9UmU1qzs4rbo1_r3_250.jpg'
+ masthead_url = 'http://www.hellercd.com/wp-content/uploads/2010/09/hero.jpg'
remove_tags_before = dict(id='item')
remove_tags_after = dict(id='item')
remove_tags = [dict(attrs={'class':['sidebar', 'about', 'footer', 'description,' 'disqus', 'nav', 'notes', 'disqus_thread']}),
@@ -21,4 +22,3 @@ class AdvancedUserRecipe1293122276(BasicNewsRecipe):
feeds = [(u'Smarter Planet Tumblr', u'http://smarterplanet.tumblr.com/mobile/rss')]
-
diff --git a/src/calibre/devices/hanvon/driver.py b/src/calibre/devices/hanvon/driver.py
index 1fe18afc58..f9dec178c6 100644
--- a/src/calibre/devices/hanvon/driver.py
+++ b/src/calibre/devices/hanvon/driver.py
@@ -24,7 +24,7 @@ class N516(USBMS):
supported_platforms = ['windows', 'osx', 'linux']
# Ordered list of supported formats
- FORMATS = ['epub', 'prc', 'html', 'pdf', 'txt']
+ FORMATS = ['epub', 'prc', 'mobi', 'html', 'pdf', 'txt']
VENDOR_ID = [0x0525]
PRODUCT_ID = [0xa4a5]
diff --git a/src/calibre/ebooks/conversion/plumber.py b/src/calibre/ebooks/conversion/plumber.py
index dd527ea0b5..5807ba5f8f 100644
--- a/src/calibre/ebooks/conversion/plumber.py
+++ b/src/calibre/ebooks/conversion/plumber.py
@@ -576,10 +576,12 @@ OptionRecommendation(name='sr3_replace',
if not input_fmt:
raise ValueError('Input file must have an extension')
input_fmt = input_fmt[1:].lower()
+ self.archive_input_tdir = None
if input_fmt in ('zip', 'rar', 'oebzip'):
self.log('Processing archive...')
- tdir = PersistentTemporaryDirectory('_plumber')
+ tdir = PersistentTemporaryDirectory('_plumber_archive')
self.input, input_fmt = self.unarchive(self.input, tdir)
+ self.archive_input_tdir = tdir
if os.access(self.input, os.R_OK):
nfp = run_plugins_on_preprocess(self.input, input_fmt)
if nfp != self.input:
diff --git a/src/calibre/ebooks/metadata/covers.py b/src/calibre/ebooks/metadata/covers.py
index 2f6fb46540..cbd8fc0e99 100644
--- a/src/calibre/ebooks/metadata/covers.py
+++ b/src/calibre/ebooks/metadata/covers.py
@@ -121,6 +121,7 @@ class LibraryThingCovers(CoverDownload): # {{{
LIBRARYTHING = 'http://www.librarything.com/isbn/'
def get_cover_url(self, isbn, br, timeout=5.):
+
try:
src = br.open_novisit('http://www.librarything.com/isbn/'+isbn,
timeout=timeout).read().decode('utf-8', 'replace')
@@ -129,6 +130,8 @@ class LibraryThingCovers(CoverDownload): # {{{
err = Exception(_('LibraryThing.com timed out. Try again later.'))
raise err
else:
+ if '/wiki/index.php/HelpThing:Verify' in src:
+ raise Exception('LibraryThing is blocking calibre.')
s = BeautifulSoup(src)
url = s.find('td', attrs={'class':'left'})
if url is None:
@@ -142,9 +145,12 @@ class LibraryThingCovers(CoverDownload): # {{{
return url
def has_cover(self, mi, ans, timeout=5.):
- if not mi.isbn:
+ if not mi.isbn or not self.site_customization:
return False
- br = browser()
+ from calibre.ebooks.metadata.library_thing import get_browser, login
+ br = get_browser()
+ un, _, pw = self.site_customization.partition(':')
+ login(br, un, pw)
try:
self.get_cover_url(mi.isbn, br, timeout=timeout)
self.debug('cover for', mi.isbn, 'found')
@@ -153,9 +159,12 @@ class LibraryThingCovers(CoverDownload): # {{{
self.debug(e)
def get_covers(self, mi, result_queue, abort, timeout=5.):
- if not mi.isbn:
+ if not mi.isbn or not self.site_customization:
return
- br = browser()
+ from calibre.ebooks.metadata.library_thing import get_browser, login
+ br = get_browser()
+ un, _, pw = self.site_customization.partition(':')
+ login(br, un, pw)
try:
url = self.get_cover_url(mi.isbn, br, timeout=timeout)
cover_data = br.open_novisit(url).read()
@@ -164,6 +173,11 @@ class LibraryThingCovers(CoverDownload): # {{{
result_queue.put((False, self.exception_to_string(e),
traceback.format_exc(), self.name))
+ def customization_help(self, gui=False):
+ ans = _('To use librarything.com you must sign up for a %sfree account%s '
+ 'and enter your username and password separated by a : below.')
+ return '
'+ans%('', '')
+
# }}}
def check_for_cover(mi, timeout=5.): # {{{
diff --git a/src/calibre/ebooks/metadata/fetch.py b/src/calibre/ebooks/metadata/fetch.py
index 8018f42b13..bd8d96a399 100644
--- a/src/calibre/ebooks/metadata/fetch.py
+++ b/src/calibre/ebooks/metadata/fetch.py
@@ -251,19 +251,26 @@ class LibraryThing(MetadataSource): # {{{
name = 'LibraryThing'
metadata_type = 'social'
- description = _('Downloads series/tags/rating information from librarything.com')
+ description = _('Downloads series/covers/rating information from librarything.com')
def fetch(self):
- if not self.isbn:
+ if not self.isbn or not self.site_customization:
return
from calibre.ebooks.metadata.library_thing import get_social_metadata
+ un, _, pw = self.site_customization.partition(':')
try:
self.results = get_social_metadata(self.title, self.book_author,
- self.publisher, self.isbn)
+ self.publisher, self.isbn, username=un, password=pw)
except Exception, e:
self.exception = e
self.tb = traceback.format_exc()
+ @property
+ def string_customization_help(self):
+ ans = _('To use librarything.com you must sign up for a %sfree account%s '
+ 'and enter your username and password separated by a : below.')
+ return '
'+ans%('', '')
+
# }}}
diff --git a/src/calibre/ebooks/metadata/library_thing.py b/src/calibre/ebooks/metadata/library_thing.py
index d956747a2b..a0f28a3c21 100644
--- a/src/calibre/ebooks/metadata/library_thing.py
+++ b/src/calibre/ebooks/metadata/library_thing.py
@@ -4,14 +4,13 @@ __copyright__ = '2008, Kovid Goyal '
Fetch cover from LibraryThing.com based on ISBN number.
'''
-import sys, socket, os, re, random
+import sys, re, random
from lxml import html
import mechanize
from calibre import browser, prints
from calibre.utils.config import OptionParser
-from calibre.ebooks.BeautifulSoup import BeautifulSoup
from calibre.ebooks.chardet import strip_encoding_declarations
OPENLIBRARY = 'http://covers.openlibrary.org/b/isbn/%s-L.jpg?default=false'
@@ -28,6 +27,12 @@ def get_ua():
]
return choices[random.randint(0, len(choices)-1)]
+_lt_br = None
+def get_browser():
+ global _lt_br
+ if _lt_br is None:
+ _lt_br = browser(user_agent=get_ua())
+ return _lt_br.clone_browser()
class HeadRequest(mechanize.Request):
@@ -35,7 +40,7 @@ class HeadRequest(mechanize.Request):
return 'HEAD'
def check_for_cover(isbn, timeout=5.):
- br = browser(user_agent=get_ua())
+ br = get_browser()
br.set_handle_redirect(False)
try:
br.open_novisit(HeadRequest(OPENLIBRARY%isbn), timeout=timeout)
@@ -54,46 +59,16 @@ class ISBNNotFound(LibraryThingError):
class ServerBusy(LibraryThingError):
pass
-def login(br, username, password, force=True):
- br.open('http://www.librarything.com')
+def login(br, username, password):
+ raw = br.open('http://www.librarything.com').read()
+ if '>Sign out' in raw:
+ return
br.select_form('signup')
br['formusername'] = username
br['formpassword'] = password
- br.submit()
-
-
-def cover_from_isbn(isbn, timeout=5., username=None, password=None):
- src = None
- br = browser(user_agent=get_ua())
- try:
- return br.open(OPENLIBRARY%isbn, timeout=timeout).read(), 'jpg'
- except:
- pass # Cover not found
- if username and password:
- try:
- login(br, username, password, force=False)
- except:
- pass
- try:
- src = br.open_novisit('http://www.librarything.com/isbn/'+isbn,
- timeout=timeout).read().decode('utf-8', 'replace')
- except Exception, err:
- if isinstance(getattr(err, 'args', [None])[0], socket.timeout):
- err = LibraryThingError(_('LibraryThing.com timed out. Try again later.'))
- raise err
- else:
- s = BeautifulSoup(src)
- url = s.find('td', attrs={'class':'left'})
- if url is None:
- if s.find('div', attrs={'class':'highloadwarning'}) is not None:
- raise ServerBusy(_('Could not fetch cover as server is experiencing high load. Please try again later.'))
- raise ISBNNotFound('ISBN: '+isbn+_(' not found.'))
- url = url.find('img')
- if url is None:
- raise LibraryThingError(_('LibraryThing.com server error. Try again later.'))
- url = re.sub(r'_S[XY]\d+', '', url['src'])
- cover_data = br.open_novisit(url).read()
- return cover_data, url.rpartition('.')[-1]
+ raw = br.submit().read()
+ if '>Sign out' not in raw:
+ raise ValueError('Failed to login as %r:%r'%(username, password))
def option_parser():
parser = OptionParser(usage=\
@@ -113,15 +88,16 @@ def get_social_metadata(title, authors, publisher, isbn, username=None,
from calibre.ebooks.metadata import MetaInformation
mi = MetaInformation(title, authors)
if isbn:
- br = browser(user_agent=get_ua())
- if username and password:
- try:
- login(br, username, password, force=False)
- except:
- pass
+ br = get_browser()
+ try:
+ login(br, username, password)
- raw = br.open_novisit('http://www.librarything.com/isbn/'
- +isbn).read()
+ raw = br.open_novisit('http://www.librarything.com/isbn/'
+ +isbn).read()
+ except:
+ return mi
+ if '/wiki/index.php/HelpThing:Verify' in raw:
+ raise Exception('LibraryThing is blocking calibre.')
if not raw:
return mi
raw = raw.decode('utf-8', 'replace')
@@ -172,15 +148,46 @@ def main(args=sys.argv):
parser.print_help()
return 1
isbn = args[1]
- mi = get_social_metadata('', [], '', isbn)
+ from calibre.customize.ui import metadata_sources, cover_sources
+ lt = None
+ for x in metadata_sources('social'):
+ if x.name == 'LibraryThing':
+ lt = x
+ break
+ lt('', '', '', isbn, True)
+ lt.join()
+ if lt.exception:
+ print lt.tb
+ return 1
+ mi = lt.results
prints(mi)
- cover_data, ext = cover_from_isbn(isbn, username=opts.username,
- password=opts.password)
- if not ext:
- ext = 'jpg'
- oname = os.path.abspath(isbn+'.'+ext)
- open(oname, 'w').write(cover_data)
- print 'Cover saved to file', oname
+ mi.isbn = isbn
+
+ lt = None
+ for x in cover_sources():
+ if x.name == 'librarything.com covers':
+ lt = x
+ break
+
+ from threading import Event
+ from Queue import Queue
+ ev = Event()
+ lt.has_cover(mi, ev)
+ hc = ev.is_set()
+ print 'Has cover:', hc
+ if hc:
+ abort = Event()
+ temp = Queue()
+ lt.get_covers(mi, temp, abort)
+
+ cover = temp.get_nowait()
+ if cover[0]:
+ open(isbn + '.jpg', 'wb').write(cover[1])
+ print 'Cover saved to:', isbn+'.jpg'
+ else:
+ print 'Cover download failed'
+ print cover[2]
+
return 0
if __name__ == '__main__':
diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py
index 84a26cea18..a8f80ab35a 100644
--- a/src/calibre/gui2/__init__.py
+++ b/src/calibre/gui2/__init__.py
@@ -8,12 +8,12 @@ from urllib import unquote
from PyQt4.Qt import QVariant, QFileInfo, QObject, SIGNAL, QBuffer, Qt, \
QByteArray, QTranslator, QCoreApplication, QThread, \
QEvent, QTimer, pyqtSignal, QDate, QDesktopServices, \
- QFileDialog, QMessageBox, QPixmap, QFileIconProvider, \
- QIcon, QApplication, QDialog, QPushButton, QUrl, QFont
+ QFileDialog, QFileIconProvider, \
+ QIcon, QApplication, QDialog, QUrl, QFont
ORG_NAME = 'KovidsBrain'
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.localization import set_qt_translator
from calibre.ebooks.metadata.meta import get_metadata, metadata_from_formats
@@ -178,104 +178,40 @@ def is_widescreen():
def extension(path):
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,
show_copy_button=True):
- d = MessageBox(QMessageBox.Warning, 'WARNING: '+title, msg, QMessageBox.Ok,
- parent, det_msg)
- d.setEscapeButton(QMessageBox.Ok)
- d.setIconPixmap(QPixmap(I('dialog_warning.png')))
- if not show_copy_button:
- d.cb.setVisible(False)
+ from calibre.gui2.dialogs.message_box import MessageBox
+ d = MessageBox(MessageBox.WARNING, 'WARNING: '+title, msg, det_msg, parent=parent,
+ show_copy_button=show_copy_button)
if show:
return d.exec_()
return d
def error_dialog(parent, title, msg, det_msg='', show=False,
show_copy_button=True):
- d = MessageBox(QMessageBox.Critical, 'ERROR: '+title, msg, QMessageBox.Ok,
- parent, det_msg)
- d.setIconPixmap(QPixmap(I('dialog_error.png')))
- d.setEscapeButton(QMessageBox.Ok)
- if not show_copy_button:
- d.cb.setVisible(False)
+ from calibre.gui2.dialogs.message_box import MessageBox
+ d = MessageBox(MessageBox.ERROR, 'ERROR: '+title, msg, det_msg, parent=parent,
+ show_copy_button=show_copy_button)
if show:
return d.exec_()
return d
-def question_dialog(parent, title, msg, det_msg='', show_copy_button=True,
- buttons=QMessageBox.Yes|QMessageBox.No, yes_button=QMessageBox.Yes):
- d = MessageBox(QMessageBox.Question, title, msg, buttons,
- parent, det_msg)
- d.setIconPixmap(QPixmap(I('dialog_question.png')))
- d.setEscapeButton(QMessageBox.No)
- if not show_copy_button:
- d.cb.setVisible(False)
+def question_dialog(parent, title, msg, det_msg='', show_copy_button=False,
+ buttons=None, yes_button=None):
+ from calibre.gui2.dialogs.message_box import MessageBox
+ d = MessageBox(MessageBox.QUESTION, title, msg, det_msg, parent=parent,
+ show_copy_button=show_copy_button)
+ if buttons is not None:
+ d.bb.setStandardButtons(buttons)
- return d.exec_() == yes_button
+ return d.exec_() == d.Accepted
def info_dialog(parent, title, msg, det_msg='', show=False,
show_copy_button=True):
- d = MessageBox(QMessageBox.Information, title, msg, QMessageBox.Ok,
- parent, det_msg)
- d.setIconPixmap(QPixmap(I('dialog_information.png')))
- if not show_copy_button:
- d.cb.setVisible(False)
+ from calibre.gui2.dialogs.message_box import MessageBox
+ d = MessageBox(MessageBox.INFO, title, msg, det_msg, parent=parent,
+ show_copy_button=show_copy_button)
if show:
return d.exec_()
diff --git a/src/calibre/gui2/convert/bulk.py b/src/calibre/gui2/convert/bulk.py
index 591ac92b2b..349f39ac76 100644
--- a/src/calibre/gui2/convert/bulk.py
+++ b/src/calibre/gui2/convert/bulk.py
@@ -4,6 +4,8 @@ __license__ = 'GPL 3'
__copyright__ = '2009, John Schember '
__docformat__ = 'restructuredtext en'
+import shutil
+
from PyQt4.Qt import QString, SIGNAL
from calibre.gui2.convert.single import Config, sort_formats_by_preference, \
@@ -108,6 +110,11 @@ class BulkConfig(Config):
idx = oidx if -1 < oidx < self._groups_model.rowCount() else 0
self.groups.setCurrentIndex(self._groups_model.index(idx))
self.stack.setCurrentIndex(idx)
+ try:
+ shutil.rmtree(self.plumber.archive_input_tdir, ignore_errors=True)
+ except:
+ pass
+
def setup_output_formats(self, db, preferred_output_format):
if preferred_output_format:
diff --git a/src/calibre/gui2/convert/single.py b/src/calibre/gui2/convert/single.py
index da58de545b..59fcbb65ad 100644
--- a/src/calibre/gui2/convert/single.py
+++ b/src/calibre/gui2/convert/single.py
@@ -6,7 +6,7 @@ __license__ = 'GPL v3'
__copyright__ = '2009, Kovid Goyal '
__docformat__ = 'restructuredtext en'
-import sys, cPickle
+import sys, cPickle, shutil
from PyQt4.Qt import QString, SIGNAL, QAbstractListModel, Qt, QVariant, QFont
@@ -224,6 +224,10 @@ class Config(ResizableDialog, Ui_Dialog):
idx = oidx if -1 < oidx < self._groups_model.rowCount() else 0
self.groups.setCurrentIndex(self._groups_model.index(idx))
self.stack.setCurrentIndex(idx)
+ try:
+ shutil.rmtree(self.plumber.archive_input_tdir, ignore_errors=True)
+ except:
+ pass
def setup_input_output_formats(self, db, book_id, preferred_input_format,
diff --git a/src/calibre/gui2/dialogs/message_box.py b/src/calibre/gui2/dialogs/message_box.py
new file mode 100644
index 0000000000..123476b734
--- /dev/null
+++ b/src/calibre/gui2/dialogs/message_box.py
@@ -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 '
+__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 goog ',
+ det_msg='det '*1000,
+ show_copy_button=True)
diff --git a/src/calibre/gui2/dialogs/message_box.ui b/src/calibre/gui2/dialogs/message_box.ui
new file mode 100644
index 0000000000..136e6d250e
--- /dev/null
+++ b/src/calibre/gui2/dialogs/message_box.ui
@@ -0,0 +1,105 @@
+
+
+ Dialog
+
+
+
+ 0
+ 0
+ 497
+ 235
+
+
+
+ Dialog
+
+
+ -
+
+
+
+ 68
+ 68
+
+
+
+
+
+
+ :/images/dialog_warning.png
+
+
+ true
+
+
+
+ -
+
+
+
+
+
+ true
+
+
+ true
+
+
+
+ -
+
+
+ true
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Ok
+
+
+
+
+
+
+
+
+
+
+ bb
+ accepted()
+ Dialog
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ bb
+ rejected()
+ Dialog
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+