Merge from trunk

This commit is contained in:
Charles Haley 2011-01-27 08:44:02 +00:00
commit cda3451390
11 changed files with 340 additions and 154 deletions

View File

@ -1,17 +1,18 @@
from calibre.web.feeds.news import BasicNewsRecipe from calibre.web.feeds.news import BasicNewsRecipe
class AdvancedUserRecipe1293122276(BasicNewsRecipe): class AdvancedUserRecipe1293122276(BasicNewsRecipe):
title = u'Smarter Planet | Tumblr for eReaders' title = u'Smarter Planet | Tumblr'
__author__ = 'Jack Mason' __author__ = 'Jack Mason'
author = 'IBM Global Business Services' author = 'IBM Global Business Services'
publisher = 'IBM' publisher = 'IBM'
language = 'en' language = 'en'
category = 'news, technology, IT, internet of things, analytics' category = 'news, technology, IT, internet of things, analytics'
oldest_article = 7 oldest_article = 14
max_articles_per_feed = 30 max_articles_per_feed = 30
no_stylesheets = True no_stylesheets = True
use_embedded_content = False 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_before = dict(id='item')
remove_tags_after = dict(id='item') remove_tags_after = dict(id='item')
remove_tags = [dict(attrs={'class':['sidebar', 'about', 'footer', 'description,' 'disqus', 'nav', 'notes', 'disqus_thread']}), 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')] feeds = [(u'Smarter Planet Tumblr', u'http://smarterplanet.tumblr.com/mobile/rss')]

View File

@ -24,7 +24,7 @@ class N516(USBMS):
supported_platforms = ['windows', 'osx', 'linux'] supported_platforms = ['windows', 'osx', 'linux']
# Ordered list of supported formats # Ordered list of supported formats
FORMATS = ['epub', 'prc', 'html', 'pdf', 'txt'] FORMATS = ['epub', 'prc', 'mobi', 'html', 'pdf', 'txt']
VENDOR_ID = [0x0525] VENDOR_ID = [0x0525]
PRODUCT_ID = [0xa4a5] PRODUCT_ID = [0xa4a5]

View File

@ -576,10 +576,12 @@ OptionRecommendation(name='sr3_replace',
if not input_fmt: if not input_fmt:
raise ValueError('Input file must have an extension') raise ValueError('Input file must have an extension')
input_fmt = input_fmt[1:].lower() input_fmt = input_fmt[1:].lower()
self.archive_input_tdir = None
if input_fmt in ('zip', 'rar', 'oebzip'): if input_fmt in ('zip', 'rar', 'oebzip'):
self.log('Processing archive...') self.log('Processing archive...')
tdir = PersistentTemporaryDirectory('_plumber') tdir = PersistentTemporaryDirectory('_plumber_archive')
self.input, input_fmt = self.unarchive(self.input, tdir) self.input, input_fmt = self.unarchive(self.input, tdir)
self.archive_input_tdir = tdir
if os.access(self.input, os.R_OK): if os.access(self.input, os.R_OK):
nfp = run_plugins_on_preprocess(self.input, input_fmt) nfp = run_plugins_on_preprocess(self.input, input_fmt)
if nfp != self.input: if nfp != self.input:

View File

@ -121,6 +121,7 @@ class LibraryThingCovers(CoverDownload): # {{{
LIBRARYTHING = 'http://www.librarything.com/isbn/' LIBRARYTHING = 'http://www.librarything.com/isbn/'
def get_cover_url(self, isbn, br, timeout=5.): def get_cover_url(self, isbn, br, timeout=5.):
try: try:
src = br.open_novisit('http://www.librarything.com/isbn/'+isbn, src = br.open_novisit('http://www.librarything.com/isbn/'+isbn,
timeout=timeout).read().decode('utf-8', 'replace') timeout=timeout).read().decode('utf-8', 'replace')
@ -129,6 +130,8 @@ class LibraryThingCovers(CoverDownload): # {{{
err = Exception(_('LibraryThing.com timed out. Try again later.')) err = Exception(_('LibraryThing.com timed out. Try again later.'))
raise err raise err
else: else:
if '/wiki/index.php/HelpThing:Verify' in src:
raise Exception('LibraryThing is blocking calibre.')
s = BeautifulSoup(src) s = BeautifulSoup(src)
url = s.find('td', attrs={'class':'left'}) url = s.find('td', attrs={'class':'left'})
if url is None: if url is None:
@ -142,9 +145,12 @@ class LibraryThingCovers(CoverDownload): # {{{
return url return url
def has_cover(self, mi, ans, timeout=5.): def has_cover(self, mi, ans, timeout=5.):
if not mi.isbn: if not mi.isbn or not self.site_customization:
return False 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: try:
self.get_cover_url(mi.isbn, br, timeout=timeout) self.get_cover_url(mi.isbn, br, timeout=timeout)
self.debug('cover for', mi.isbn, 'found') self.debug('cover for', mi.isbn, 'found')
@ -153,9 +159,12 @@ class LibraryThingCovers(CoverDownload): # {{{
self.debug(e) self.debug(e)
def get_covers(self, mi, result_queue, abort, timeout=5.): def get_covers(self, mi, result_queue, abort, timeout=5.):
if not mi.isbn: if not mi.isbn or not self.site_customization:
return 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: try:
url = self.get_cover_url(mi.isbn, br, timeout=timeout) url = self.get_cover_url(mi.isbn, br, timeout=timeout)
cover_data = br.open_novisit(url).read() cover_data = br.open_novisit(url).read()
@ -164,6 +173,11 @@ class LibraryThingCovers(CoverDownload): # {{{
result_queue.put((False, self.exception_to_string(e), result_queue.put((False, self.exception_to_string(e),
traceback.format_exc(), self.name)) 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 '<p>'+ans%('<a href="http://www.librarything.com">', '</a>')
# }}} # }}}
def check_for_cover(mi, timeout=5.): # {{{ def check_for_cover(mi, timeout=5.): # {{{

View File

@ -251,19 +251,26 @@ class LibraryThing(MetadataSource): # {{{
name = 'LibraryThing' name = 'LibraryThing'
metadata_type = 'social' metadata_type = 'social'
description = _('Downloads series/tags/rating information from librarything.com') description = _('Downloads series/covers/rating information from librarything.com')
def fetch(self): def fetch(self):
if not self.isbn: if not self.isbn or not self.site_customization:
return return
from calibre.ebooks.metadata.library_thing import get_social_metadata from calibre.ebooks.metadata.library_thing import get_social_metadata
un, _, pw = self.site_customization.partition(':')
try: try:
self.results = get_social_metadata(self.title, self.book_author, 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: except Exception, e:
self.exception = e self.exception = e
self.tb = traceback.format_exc() 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 '<p>'+ans%('<a href="http://www.librarything.com">', '</a>')
# }}} # }}}

View File

@ -4,14 +4,13 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
Fetch cover from LibraryThing.com based on ISBN number. Fetch cover from LibraryThing.com based on ISBN number.
''' '''
import sys, socket, os, re, random import sys, re, random
from lxml import html from lxml import html
import mechanize import mechanize
from calibre import browser, prints from calibre import browser, prints
from calibre.utils.config import OptionParser from calibre.utils.config import OptionParser
from calibre.ebooks.BeautifulSoup import BeautifulSoup
from calibre.ebooks.chardet import strip_encoding_declarations from calibre.ebooks.chardet import strip_encoding_declarations
OPENLIBRARY = 'http://covers.openlibrary.org/b/isbn/%s-L.jpg?default=false' 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)] 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): class HeadRequest(mechanize.Request):
@ -35,7 +40,7 @@ class HeadRequest(mechanize.Request):
return 'HEAD' return 'HEAD'
def check_for_cover(isbn, timeout=5.): def check_for_cover(isbn, timeout=5.):
br = browser(user_agent=get_ua()) br = get_browser()
br.set_handle_redirect(False) br.set_handle_redirect(False)
try: try:
br.open_novisit(HeadRequest(OPENLIBRARY%isbn), timeout=timeout) br.open_novisit(HeadRequest(OPENLIBRARY%isbn), timeout=timeout)
@ -54,46 +59,16 @@ class ISBNNotFound(LibraryThingError):
class ServerBusy(LibraryThingError): class ServerBusy(LibraryThingError):
pass pass
def login(br, username, password, force=True): def login(br, username, password):
br.open('http://www.librarything.com') raw = br.open('http://www.librarything.com').read()
if '>Sign out' in raw:
return
br.select_form('signup') br.select_form('signup')
br['formusername'] = username br['formusername'] = username
br['formpassword'] = password br['formpassword'] = password
br.submit() raw = br.submit().read()
if '>Sign out' not in raw:
raise ValueError('Failed to login as %r:%r'%(username, password))
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]
def option_parser(): def option_parser():
parser = OptionParser(usage=\ parser = OptionParser(usage=\
@ -113,15 +88,16 @@ def get_social_metadata(title, authors, publisher, isbn, username=None,
from calibre.ebooks.metadata import MetaInformation from calibre.ebooks.metadata import MetaInformation
mi = MetaInformation(title, authors) mi = MetaInformation(title, authors)
if isbn: if isbn:
br = browser(user_agent=get_ua()) br = get_browser()
if username and password: try:
try: login(br, username, password)
login(br, username, password, force=False)
except:
pass
raw = br.open_novisit('http://www.librarything.com/isbn/' raw = br.open_novisit('http://www.librarything.com/isbn/'
+isbn).read() +isbn).read()
except:
return mi
if '/wiki/index.php/HelpThing:Verify' in raw:
raise Exception('LibraryThing is blocking calibre.')
if not raw: if not raw:
return mi return mi
raw = raw.decode('utf-8', 'replace') raw = raw.decode('utf-8', 'replace')
@ -172,15 +148,46 @@ def main(args=sys.argv):
parser.print_help() parser.print_help()
return 1 return 1
isbn = args[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) prints(mi)
cover_data, ext = cover_from_isbn(isbn, username=opts.username, mi.isbn = isbn
password=opts.password)
if not ext: lt = None
ext = 'jpg' for x in cover_sources():
oname = os.path.abspath(isbn+'.'+ext) if x.name == 'librarything.com covers':
open(oname, 'w').write(cover_data) lt = x
print 'Cover saved to file', oname 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 return 0
if __name__ == '__main__': if __name__ == '__main__':

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

@ -4,6 +4,8 @@ __license__ = 'GPL 3'
__copyright__ = '2009, John Schember <john@nachtimwald.com>' __copyright__ = '2009, John Schember <john@nachtimwald.com>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
import shutil
from PyQt4.Qt import QString, SIGNAL from PyQt4.Qt import QString, SIGNAL
from calibre.gui2.convert.single import Config, sort_formats_by_preference, \ 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 idx = oidx if -1 < oidx < self._groups_model.rowCount() else 0
self.groups.setCurrentIndex(self._groups_model.index(idx)) self.groups.setCurrentIndex(self._groups_model.index(idx))
self.stack.setCurrentIndex(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): def setup_output_formats(self, db, preferred_output_format):
if preferred_output_format: if preferred_output_format:

View File

@ -6,7 +6,7 @@ __license__ = 'GPL v3'
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>' __copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
import sys, cPickle import sys, cPickle, shutil
from PyQt4.Qt import QString, SIGNAL, QAbstractListModel, Qt, QVariant, QFont 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 idx = oidx if -1 < oidx < self._groups_model.rowCount() else 0
self.groups.setCurrentIndex(self._groups_model.index(idx)) self.groups.setCurrentIndex(self._groups_model.index(idx))
self.stack.setCurrentIndex(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, def setup_input_output_formats(self, db, book_id, preferred_input_format,

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>