Merge from main branch

This commit is contained in:
Tom Scholl 2011-04-09 23:00:14 +00:00
commit 54b3098eb2
18 changed files with 647 additions and 207 deletions

32
recipes/dvhn.recipe Normal file
View File

@ -0,0 +1,32 @@
from calibre.web.feeds.news import BasicNewsRecipe
class AdvancedUserRecipe1302341394(BasicNewsRecipe):
title = u'DvhN'
oldest_article = 1
max_articles_per_feed = 200
__author__ = 'Reijndert'
no_stylesheets = True
cover_url = 'http://www.dvhn.nl/template/Dagblad_v2.0/gfx/logo_DvhN.gif'
language = 'nl'
country = 'NL'
version = 1
publisher = u'Dagblad van het Noorden'
category = u'Nieuws'
description = u'Nieuws uit Noord Nederland'
keep_only_tags = [dict(name='div', attrs={'id':'fullPicture'})
,dict(name='div', attrs={'id':'articleText'})
]
remove_tags = [
dict(name=['object','link','iframe','base'])
,dict(name='span',attrs={'class':'copyright'})
]
feeds = [(u'Drenthe', u'http://www.dvhn.nl/nieuws/drenthe/index.jsp?service=rss'), (u'Groningen', u'http://www.dvhn.nl/nieuws/groningen/index.jsp?service=rss'), (u'Nederland', u'http://www.dvhn.nl/nieuws/nederland/index.jsp?service=rss'), (u'Wereld', u'http://www.dvhn.nl/nieuws/wereld/index.jsp?service=rss'), (u'Economie', u'http://www.dvhn.nl/nieuws/economie/index.jsp?service=rss'), (u'Sport', u'http://www.dvhn.nl/nieuws/sport/index.jsp?service=rss'), (u'Cultuur', u'http://www.dvhn.nl/nieuws/kunst/index.jsp?service=rss'), (u'24 Uur', u'http://www.dvhn.nl/nieuws/24uurdvhn/index.jsp?service=rss&selectiontype=last24hours')]
extra_css = '''
body {font-family: verdana, arial, helvetica, geneva, sans-serif;}
'''

View File

@ -88,13 +88,6 @@ categories_collapsed_rating_template = r'{first.avg_rating:4.2f:ifempty(0)} - {l
categories_collapsed_popularity_template = r'{first.count:d} - {last.count:d}'
#: Set boolean custom columns to be tristate
# Set whether boolean custom columns are two- or three-valued.
# Two-values for true booleans
# three-values for yes/no/unknown
# Set to 'yes' for three-values, 'no' for two-values
bool_custom_columns_are_tristate = 'yes'
#: Specify columns to sort the booklist by on startup
# Provide a set of columns to be sorted on when calibre starts
# The argument is None if saved sort history is to be used

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -26,9 +26,9 @@ class EDGE(USBMS):
PRODUCT_ID = [0x0c02]
BCD = [0x0223]
VENDOR_NAME = 'ANDROID'
WINDOWS_MAIN_MEM = '__FILE-STOR_GADG'
WINDOWS_CARD_A_MEM = '__FILE-STOR_GADG'
VENDOR_NAME = ['ANDROID', 'LINUX']
WINDOWS_MAIN_MEM = ['__FILE-STOR_GADG', 'FILE-CD_GADGET']
WINDOWS_CARD_A_MEM = ['__FILE-STOR_GADG', 'FILE-CD_GADGET']
MAIN_MEMORY_VOLUME_LABEL = 'Edge Main Memory'
STORAGE_CARD_VOLUME_LABEL = 'Edge Storage Card'

View File

@ -279,7 +279,7 @@ class Worker(Thread): # Get details {{{
class Amazon(Source):
name = 'Amazon Store'
name = 'Amazon Web'
description = _('Downloads metadata from Amazon')
capabilities = frozenset(['identify', 'cover'])

View File

@ -13,7 +13,7 @@ from functools import partial
from calibre.ebooks import ConversionError, DRMError
from calibre.ptempfile import PersistentTemporaryFile
from calibre import isosx, iswindows, islinux, isfreebsd
from calibre.constants import isosx, iswindows, islinux, isfreebsd
from calibre import CurrentDir
PDFTOHTML = 'pdftohtml'
@ -43,6 +43,8 @@ def pdftohtml(output_dir, pdf_path, no_images):
# This is neccessary as pdftohtml doesn't always (linux) respect absolute paths
pdf_path = os.path.abspath(pdf_path)
cmd = [PDFTOHTML, '-enc', 'UTF-8', '-noframes', '-p', '-nomerge', '-nodrm', '-q', pdf_path, os.path.basename(index)]
if isfreebsd:
cmd.remove('-nodrm')
if no_images:
cmd.append('-i')

View File

@ -13,7 +13,7 @@ from PyQt4.Qt import QVariant, QFileInfo, QObject, SIGNAL, QBuffer, Qt, \
ORG_NAME = 'KovidsBrain'
APP_UID = 'libprs500'
from calibre.constants import islinux, iswindows, isfreebsd, isfrozen
from calibre.constants import islinux, iswindows, isfreebsd, isfrozen, isosx
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
@ -23,25 +23,45 @@ from calibre.utils.date import UNDEFINED_DATE
# Setup gprefs {{{
gprefs = JSONConfig('gui')
gprefs.defaults['action-layout-menubar'] = ()
gprefs.defaults['action-layout-menubar-device'] = ()
gprefs.defaults['action-layout-toolbar'] = (
if isosx:
gprefs.defaults['action-layout-menubar'] = (
'Add Books', 'Edit Metadata', 'Convert Books',
'Choose Library', 'Save To Disk', 'Preferences',
'Help',
)
gprefs.defaults['action-layout-menubar-device'] = (
'Add Books', 'Edit Metadata', 'Convert Books',
'Location Manager', 'Send To Device',
'Save To Disk', 'Preferences', 'Help',
)
gprefs.defaults['action-layout-toolbar'] = (
'Add Books', 'Edit Metadata', None, 'Convert Books', 'View', None,
'Choose Library', 'Donate', None, 'Fetch News', 'Save To Disk',
'Connect Share', None, 'Remove Books',
)
gprefs.defaults['action-layout-toolbar-device'] = (
'Add Books', 'Edit Metadata', None, 'Convert Books', 'View',
'Send To Device', None, None, 'Location Manager', None, None,
'Fetch News', 'Save To Disk', 'Connect Share', None,
'Remove Books',
)
else:
gprefs.defaults['action-layout-menubar'] = ()
gprefs.defaults['action-layout-menubar-device'] = ()
gprefs.defaults['action-layout-toolbar'] = (
'Add Books', 'Edit Metadata', None, 'Convert Books', 'View', None,
'Choose Library', 'Donate', None, 'Fetch News', 'Save To Disk',
'Connect Share', None, 'Remove Books', None, 'Help', 'Preferences',
)
gprefs.defaults['action-layout-toolbar-child'] = ()
gprefs.defaults['action-layout-toolbar-device'] = (
gprefs.defaults['action-layout-toolbar-device'] = (
'Add Books', 'Edit Metadata', None, 'Convert Books', 'View',
'Send To Device', None, None, 'Location Manager', None, None,
'Fetch News', 'Save To Disk', 'Connect Share', None,
'Remove Books', None, 'Help', 'Preferences',
)
gprefs.defaults['action-layout-toolbar-child'] = ()
gprefs.defaults['action-layout-context-menu'] = (
'Edit Metadata', 'Send To Device', 'Save To Disk',
'Connect Share', 'Copy To Library', None,
@ -61,6 +81,7 @@ gprefs.defaults['toolbar_text'] = 'auto'
gprefs.defaults['font'] = None
gprefs.defaults['tags_browser_partition_method'] = 'first letter'
gprefs.defaults['tags_browser_collapse_at'] = 100
gprefs.defaults['edit_metadata_single_layout'] = 'default'
# }}}

View File

@ -165,6 +165,10 @@ class ConnectShareAction(InterfaceAction):
def content_server_state_changed(self, running):
self.share_conn_menu.server_state_changed(running)
if running:
self.qaction.setIcon(QIcon(I('connect_share_on.png')))
else:
self.qaction.setIcon(QIcon(I('connect_share.png')))
def toggle_content_server(self):
if self.gui.content_server is None:

View File

@ -238,7 +238,7 @@ class Spacer(QWidget): # {{{
self.l.addStretch(10)
# }}}
class MenuAction(QAction):
class MenuAction(QAction): # {{{
def __init__(self, clone, parent):
QAction.__init__(self, clone.text(), parent)
@ -247,7 +247,7 @@ class MenuAction(QAction):
def clone_changed(self):
self.setText(self.clone.text())
# }}}
class MenuBar(QMenuBar): # {{{
@ -308,22 +308,47 @@ class MenuBar(QMenuBar): # {{{
ac.setMenu(m)
return ac
# }}}
class ToolBar(QToolBar): # {{{
class BaseToolBar(QToolBar): # {{{
def __init__(self, donate, location_manager, child_bar, parent):
def __init__(self, parent):
QToolBar.__init__(self, parent)
self.gui = parent
self.child_bar = child_bar
self.setContextMenuPolicy(Qt.PreventContextMenu)
self.setMovable(False)
self.setFloatable(False)
self.setOrientation(Qt.Horizontal)
self.setAllowedAreas(Qt.TopToolBarArea|Qt.BottomToolBarArea)
self.setStyleSheet('QToolButton:checked { font-weight: bold }')
self.preferred_width = self.sizeHint().width()
def resizeEvent(self, ev):
QToolBar.resizeEvent(self, ev)
style = self.get_text_style()
self.setToolButtonStyle(style)
def get_text_style(self):
style = Qt.ToolButtonTextUnderIcon
s = gprefs['toolbar_icon_size']
if s != 'off':
p = gprefs['toolbar_text']
if p == 'never':
style = Qt.ToolButtonIconOnly
elif p == 'auto' and self.preferred_width > self.width()+35:
style = Qt.ToolButtonIconOnly
return style
def contextMenuEvent(self, *args):
pass
# }}}
class ToolBar(BaseToolBar): # {{{
def __init__(self, donate, location_manager, child_bar, parent):
BaseToolBar.__init__(self, parent)
self.gui = parent
self.child_bar = child_bar
self.donate_button = donate
self.apply_settings()
@ -333,7 +358,6 @@ class ToolBar(QToolBar): # {{{
donate.setCursor(Qt.PointingHandCursor)
self.added_actions = []
self.build_bar()
self.preferred_width = self.sizeHint().width()
self.setAcceptDrops(True)
def apply_settings(self):
@ -348,9 +372,6 @@ class ToolBar(QToolBar): # {{{
self.child_bar.setToolButtonStyle(style)
self.donate_button.set_normal_icon_size(sz, sz)
def contextMenuEvent(self, *args):
pass
def build_bar(self):
self.showing_donate = False
showing_device = self.location_manager.has_device
@ -382,21 +403,20 @@ class ToolBar(QToolBar): # {{{
bar.added_actions.append(ac)
bar.setup_tool_button(bar, ac, QToolButton.MenuButtonPopup)
elif what == 'Donate':
self.d_widget = QWidget()
self.d_widget.setLayout(QVBoxLayout())
self.d_widget.layout().addWidget(self.donate_button)
if isosx:
bar.addAction(self.gui.donate_action)
ch = self.setup_tool_button(bar, self.gui.donate_action)
ch.setText(_('Donate'))
else:
self.d_widget = QWidget()
self.d_widget.setLayout(QVBoxLayout())
self.d_widget.layout().addWidget(self.donate_button)
bar.addWidget(self.d_widget)
self.showing_donate = True
self.d_widget.setStyleSheet('QWidget, QToolButton {background-color: none; border: none; }')
bar.addWidget(self.d_widget)
self.showing_donate = True
elif what in self.gui.iactions:
action = self.gui.iactions[what]
bar.addAction(action.qaction)
self.added_actions.append(action.qaction)
self.setup_tool_button(bar, action.qaction, action.popup_type)
self.preferred_width = self.sizeHint().width()
self.child_bar.preferred_width = self.child_bar.sizeHint().width()
def setup_tool_button(self, bar, ac, menu_mode=None):
ch = bar.widgetForAction(ac)
@ -408,21 +428,6 @@ class ToolBar(QToolBar): # {{{
ch.setPopupMode(menu_mode)
return ch
def resizeEvent(self, ev):
QToolBar.resizeEvent(self, ev)
style = Qt.ToolButtonTextUnderIcon
s = gprefs['toolbar_icon_size']
if s != 'off':
p = gprefs['toolbar_text']
if p == 'never':
style = Qt.ToolButtonIconOnly
if p == 'auto' and self.preferred_width > self.width()+35 and \
not gprefs['action-layout-toolbar-child']:
style = Qt.ToolButtonIconOnly
self.setToolButtonStyle(style)
def database_changed(self, db):
pass
@ -500,7 +505,7 @@ class MainWindowMixin(object): # {{{
self.iactions['Fetch News'].init_scheduler(db)
self.search_bar = SearchBar(self)
self.child_bar = QToolBar(self)
self.child_bar = BaseToolBar(self)
self.tool_bar = ToolBar(self.donate_button,
self.location_manager, self.child_bar, self)
self.addToolBar(Qt.TopToolBarArea, self.tool_bar)

View File

@ -658,7 +658,7 @@ editors = {'default': MetadataSingleDialog, 'alt1': MetadataSingleDialogAlt1}
def edit_metadata(db, row_list, current_row, parent=None, view_slot=None,
set_current_callback=None):
cls = db.prefs.get('edit_metadata_single_layout', '')
cls = gprefs.get('edit_metadata_single_layout', '')
if cls not in editors:
cls = 'default'
d = editors[cls](db, parent)

View File

@ -9,11 +9,13 @@ __docformat__ = 'restructuredtext en'
from threading import Thread, Event
from operator import attrgetter
from Queue import Queue, Empty
from PyQt4.Qt import (QStyledItemDelegate, QTextDocument, QRectF, QIcon, Qt,
QStyle, QApplication, QDialog, QVBoxLayout, QLabel, QDialogButtonBox,
QStackedWidget, QWidget, QTableView, QGridLayout, QFontInfo, QPalette,
QTimer, pyqtSignal, QAbstractTableModel, QVariant, QSize)
QTimer, pyqtSignal, QAbstractTableModel, QVariant, QSize, QListView,
QPixmap, QAbstractListModel, QColor, QRect, QTextBrowser)
from PyQt4.QtWebKit import QWebView
from calibre.customize.ui import metadata_plugins
@ -24,6 +26,9 @@ from calibre.ebooks.metadata.book.base import Metadata
from calibre.gui2 import error_dialog, NONE
from calibre.utils.date import utcnow, fromordinal, format_date
from calibre.library.comments import comments_to_html
from calibre import force_unicode
DEBUG_DIALOG = False
class RichTextDelegate(QStyledItemDelegate): # {{{
@ -52,6 +57,65 @@ class RichTextDelegate(QStyledItemDelegate): # {{{
painter.restore()
# }}}
class CoverDelegate(QStyledItemDelegate): # {{{
needs_redraw = pyqtSignal()
def __init__(self, parent):
QStyledItemDelegate.__init__(self, parent)
self.angle = 0
self.timer = QTimer(self)
self.timer.timeout.connect(self.frame_changed)
self.color = parent.palette().color(QPalette.WindowText)
self.spinner_width = 64
def frame_changed(self, *args):
self.angle = (self.angle+30)%360
self.needs_redraw.emit()
def start_animation(self):
self.angle = 0
self.timer.start(200)
def stop_animation(self):
self.timer.stop()
def draw_spinner(self, painter, rect):
width = rect.width()
outer_radius = (width-1)*0.5
inner_radius = (width-1)*0.5*0.38
capsule_height = outer_radius - inner_radius
capsule_width = int(capsule_height * (0.23 if width > 32 else 0.35))
capsule_radius = capsule_width//2
painter.save()
painter.setRenderHint(painter.Antialiasing)
for i in xrange(12):
color = QColor(self.color)
color.setAlphaF(1.0 - (i/12.0))
painter.setPen(Qt.NoPen)
painter.setBrush(color)
painter.save()
painter.translate(rect.center())
painter.rotate(self.angle - i*30.0)
painter.drawRoundedRect(-capsule_width*0.5,
-(inner_radius+capsule_height), capsule_width,
capsule_height, capsule_radius, capsule_radius)
painter.restore()
painter.restore()
def paint(self, painter, option, index):
QStyledItemDelegate.paint(self, painter, option, index)
if self.timer.isActive() and index.data(Qt.UserRole).toBool():
rect = QRect(0, 0, self.spinner_width, self.spinner_width)
rect.moveCenter(option.rect.center())
self.draw_spinner(painter, rect)
# }}}
class ResultsModel(QAbstractTableModel): # {{{
COLUMNS = (
@ -268,7 +332,7 @@ class IdentifyWorker(Thread): # {{{
def run(self):
try:
if True:
if DEBUG_DIALOG:
self.results = self.sample_results()
else:
self.results = identify(self.log, self.abort, title=self.title,
@ -277,7 +341,7 @@ class IdentifyWorker(Thread): # {{{
result.gui_rank = i
except:
import traceback
self.error = traceback.format_exc()
self.error = force_unicode(traceback.format_exc())
# }}}
class IdentifyWidget(QWidget): # {{{
@ -398,11 +462,307 @@ class IdentifyWidget(QWidget): # {{{
self.abort.set()
# }}}
class FullFetch(QDialog): # {{{
class CoverWorker(Thread): # {{{
def __init__(self, log, abort, title, authors, identifiers):
Thread.__init__(self)
self.daemon = True
self.log, self.abort = log, abort
self.title, self.authors, self.identifiers = (title, authors,
identifiers)
self.rq = Queue()
self.error = None
def fake_run(self):
images = ['donate.png', 'config.png', 'column.png', 'eject.png', ]
import time
time.sleep(2)
for pl, im in zip(metadata_plugins(['cover']), images):
self.rq.put((pl, 1, 1, 'png', I(im, data=True)))
def run(self):
try:
if DEBUG_DIALOG:
self.fake_run()
else:
from calibre.ebooks.metadata.sources.covers import run_download
run_download(self.log, self.rq, self.abort, title=self.title,
authors=self.authors, identifiers=self.identifiers)
except:
import traceback
self.error = force_unicode(traceback.format_exc())
# }}}
class CoversModel(QAbstractListModel): # {{{
def __init__(self, current_cover, parent=None):
QAbstractListModel.__init__(self, parent)
if current_cover is None:
current_cover = QPixmap(I('default_cover.png'))
self.blank = QPixmap(I('blank.png')).scaled(150, 200)
self.covers = [self.get_item(_('Current cover'), current_cover)]
self.plugin_map = {}
for i, plugin in enumerate(metadata_plugins(['cover'])):
self.covers.append((plugin.name+'\n'+_('Searching...'),
QVariant(self.blank), None, True))
self.plugin_map[plugin] = i+1
def get_item(self, src, pmap, waiting=False):
sz = '%dx%d'%(pmap.width(), pmap.height())
text = QVariant(src + '\n' + sz)
scaled = pmap.scaled(150, 200, Qt.IgnoreAspectRatio,
Qt.SmoothTransformation)
return (text, QVariant(scaled), pmap, waiting)
def rowCount(self, parent=None):
return len(self.covers)
def data(self, index, role):
try:
text, pmap, cover, waiting = self.covers[index.row()]
except:
return NONE
if role == Qt.DecorationRole:
return pmap
if role == Qt.DisplayRole:
return text
if role == Qt.UserRole:
return waiting
return NONE
def plugin_for_index(self, index):
row = index.row() if hasattr(index, 'row') else index
for k, v in self.plugin_map.iteritems():
if v == row:
return k
def clear_failed(self):
good = []
pmap = {}
for i, x in enumerate(self.covers):
if not x[-1]:
good.append(x)
if i > 0:
plugin = self.plugin_for_index(i)
pmap[plugin] = len(good) - 1
good = [x for x in self.covers if not x[-1]]
self.covers = good
self.plugin_map = pmap
self.reset()
def index_for_plugin(self, plugin):
idx = self.plugin_map.get(plugin, 0)
return self.index(idx)
def update_result(self, plugin, width, height, data):
try:
idx = self.plugin_map[plugin]
except:
return
pmap = QPixmap()
pmap.loadFromData(data)
if pmap.isNull():
return
self.covers[idx] = self.get_item(plugin.name, pmap, waiting=False)
self.dataChanged.emit(self.index(idx), self.index(idx))
def cover_pixmap(self, index):
row = index.row()
if row > 0 and row < len(self.covers):
pmap = self.covers[row][2]
if pmap is not None and not pmap.isNull():
return pmap
# }}}
class CoversView(QListView): # {{{
chosen = pyqtSignal()
def __init__(self, current_cover, parent=None):
QListView.__init__(self, parent)
self.m = CoversModel(current_cover, self)
self.setModel(self.m)
self.setFlow(self.LeftToRight)
self.setWrapping(True)
self.setResizeMode(self.Adjust)
self.setGridSize(QSize(190, 260))
self.setIconSize(QSize(150, 200))
self.setSelectionMode(self.SingleSelection)
self.setViewMode(self.IconMode)
self.delegate = CoverDelegate(self)
self.setItemDelegate(self.delegate)
self.delegate.needs_redraw.connect(self.viewport().update,
type=Qt.QueuedConnection)
self.doubleClicked.connect(self.chosen, type=Qt.QueuedConnection)
def select(self, num):
current = self.model().index(num)
sm = self.selectionModel()
sm.select(current, sm.SelectCurrent)
def start(self):
self.select(0)
self.delegate.start_animation()
def clear_failed(self):
plugin = self.m.plugin_for_index(self.currentIndex())
self.m.clear_failed()
self.select(self.m.index_for_plugin(plugin).row())
# }}}
class CoversWidget(QWidget): # {{{
chosen = pyqtSignal()
finished = pyqtSignal()
def __init__(self, log, current_cover, parent=None):
QWidget.__init__(self, parent)
self.log = log
self.abort = Event()
self.l = l = QGridLayout()
self.setLayout(l)
self.msg = QLabel()
self.msg.setWordWrap(True)
l.addWidget(self.msg, 0, 0)
self.covers_view = CoversView(current_cover, self)
self.covers_view.chosen.connect(self.chosen)
l.addWidget(self.covers_view, 1, 0)
self.continue_processing = True
def start(self, book, current_cover, title, authors):
self.book, self.current_cover = book, current_cover
self.title, self.authors = title, authors
self.log('\n\nStarting cover download for:', book.title)
self.msg.setText('<p>'+_('Downloading covers for <b>%s</b>, please wait...')%book.title)
self.covers_view.start()
self.worker = CoverWorker(self.log, self.abort, self.title,
self.authors, book.identifiers)
self.worker.start()
QTimer.singleShot(50, self.check)
self.covers_view.setFocus(Qt.OtherFocusReason)
def check(self):
if self.worker.is_alive() and not self.abort.is_set():
QTimer.singleShot(50, self.check)
try:
self.process_result(self.worker.rq.get_nowait())
except Empty:
pass
else:
self.process_results()
def process_results(self):
while self.continue_processing:
try:
self.process_result(self.worker.rq.get_nowait())
except Empty:
break
self.covers_view.clear_failed()
if self.worker.error is not None:
error_dialog(self, _('Download failed'),
_('Failed to download any covers, click'
' "Show details" for details.'),
det_msg=self.worker.error, show=True)
num = self.covers_view.model().rowCount()
if num < 2:
txt = _('Could not find any covers for <b>%s</b>')%self.book.title
else:
txt = _('Found <b>%d</b> covers of %s. Pick the one you like'
' best.')%(num-1, self.title)
self.msg.setText(txt)
self.finished.emit()
def process_result(self, result):
if not self.continue_processing:
return
plugin, width, height, fmt, data = result
self.covers_view.model().update_result(plugin, width, height, data)
def cleanup(self):
self.covers_view.delegate.stop_animation()
self.continue_processing = False
def cancel(self):
self.continue_processing = False
self.abort.set()
def cover_pixmap(self):
idx = None
for i in self.covers_view.selectionModel().selectedIndexes():
if i.isValid():
idx = i
break
if idx is None:
idx = self.covers_view.currentIndex()
return self.covers_view.model().cover_pixmap(idx)
# }}}
class LogViewer(QDialog): # {{{
def __init__(self, log, parent=None):
QDialog.__init__(self, parent)
self.log = log
self.l = l = QVBoxLayout()
self.setLayout(l)
self.tb = QTextBrowser(self)
l.addWidget(self.tb)
self.bb = QDialogButtonBox(QDialogButtonBox.Close)
l.addWidget(self.bb)
self.bb.rejected.connect(self.reject)
self.bb.accepted.connect(self.accept)
self.setWindowTitle(_('Download log'))
self.setWindowIcon(QIcon(I('debug.png')))
self.resize(QSize(800, 400))
self.keep_updating = True
self.last_html = None
self.finished.connect(self.stop)
QTimer.singleShot(1000, self.update_log)
self.show()
def stop(self, *args):
self.keep_updating = False
def update_log(self):
if not self.keep_updating:
return
html = self.log.html
if html != self.last_html:
self.last_html = html
self.tb.setHtml('<pre>%s</pre>'%html)
QTimer.singleShot(1000, self.update_log)
# }}}
class FullFetch(QDialog): # {{{
def __init__(self, log, current_cover=None, parent=None):
QDialog.__init__(self, parent)
self.log, self.current_cover = log, current_cover
self.book = self.cover_pixmap = None
self.setWindowTitle(_('Downloading metadata...'))
self.setWindowIcon(QIcon(I('metadata.png')))
@ -418,22 +778,39 @@ class FullFetch(QDialog): # {{{
self.next_button = self.bb.addButton(_('Next'), self.bb.AcceptRole)
self.next_button.setDefault(True)
self.next_button.setEnabled(False)
self.next_button.setIcon(QIcon(I('ok.png')))
self.next_button.clicked.connect(self.next_clicked)
self.ok_button = self.bb.button(self.bb.Ok)
self.ok_button.setVisible(False)
self.ok_button.clicked.connect(self.ok_clicked)
self.log_button = self.bb.addButton(_('View log'), self.bb.ActionRole)
self.log_button.clicked.connect(self.view_log)
self.log_button.setIcon(QIcon(I('debug.png')))
self.ok_button.setVisible(False)
self.identify_widget = IdentifyWidget(log, self)
self.identify_widget.rejected.connect(self.reject)
self.identify_widget.results_found.connect(self.identify_results_found)
self.identify_widget.book_selected.connect(self.book_selected)
self.stack.addWidget(self.identify_widget)
self.resize(850, 500)
self.covers_widget = CoversWidget(self.log, self.current_cover, parent=self)
self.covers_widget.chosen.connect(self.ok_clicked)
self.stack.addWidget(self.covers_widget)
self.resize(850, 550)
self.finished.connect(self.cleanup)
def view_log(self):
self._lv = LogViewer(self.log, self)
def book_selected(self, book):
print (book)
self.next_button.setVisible(False)
self.ok_button.setVisible(True)
self.book = book
self.stack.setCurrentIndex(1)
self.covers_widget.start(book, self.current_cover,
self.title, self.authors)
def accept(self):
# Prevent the usual dialog accept mechanisms from working
@ -443,6 +820,9 @@ class FullFetch(QDialog): # {{{
self.identify_widget.cancel()
return QDialog.reject(self)
def cleanup(self):
self.covers_widget.cleanup()
def identify_results_found(self):
self.next_button.setEnabled(True)
@ -450,15 +830,25 @@ class FullFetch(QDialog): # {{{
self.identify_widget.get_result()
def ok_clicked(self, *args):
pass
self.cover_pixmap = self.covers_widget.cover_pixmap()
if DEBUG_DIALOG:
if self.cover_pixmap is not None:
self.w = QLabel()
self.w.setPixmap(self.cover_pixmap)
self.stack.addWidget(self.w)
self.stack.setCurrentIndex(2)
else:
QDialog.accept(self)
def start(self, title=None, authors=None, identifiers={}):
self.title, self.authors = title, authors
self.identify_widget.start(title=title, authors=authors,
identifiers=identifiers)
self.exec_()
# }}}
if __name__ == '__main__':
DEBUG_DIALOG = True
app = QApplication([])
d = FullFetch(Log())
d.start(title='great gatsby', authors=['Fitzgerald'])

View File

@ -11,7 +11,7 @@ from PyQt4.Qt import Qt, QVariant, QListWidgetItem
from calibre.gui2.preferences import ConfigWidgetBase, test_widget, Setting
from calibre.gui2.preferences.behavior_ui import Ui_Form
from calibre.gui2 import config, info_dialog, dynamic
from calibre.gui2 import config, info_dialog, dynamic, gprefs
from calibre.utils.config import prefs
from calibre.customize.ui import available_output_formats, all_input_formats
from calibre.utils.search_query_parser import saved_searches
@ -19,6 +19,7 @@ from calibre.ebooks import BOOK_EXTENSIONS
from calibre.ebooks.oeb.iterator import is_supported
from calibre.constants import iswindows
from calibre.utils.icu import sort_key
from calibre.utils.config import test_eight_code
class OutputFormatSetting(Setting):
@ -62,6 +63,14 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
signal = getattr(self.opt_internally_viewed_formats, 'item'+signal)
signal.connect(self.internally_viewed_formats_changed)
r('bools_are_tristate', db.prefs, restart_required=True)
if test_eight_code:
r = self.register
choices = [(_('Default'), 'default'), (_('Compact Metadata'), 'alt1')]
r('edit_metadata_single_layout', gprefs, choices=choices)
else:
self.opt_edit_metadata_single_layout.setVisible(False)
self.edit_metadata_single_label.setVisible(False)
def initialize(self):
ConfigWidgetBase.initialize(self)

View File

@ -14,44 +14,92 @@
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0" colspan="2">
<item row="0" column="1">
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>10</width>
<height>00</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="opt_overwrite_author_title_metadata">
<property name="text">
<string>&amp;Overwrite author and title by default when fetching metadata</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<item row="0" column="2">
<widget class="QCheckBox" name="opt_get_social_metadata">
<property name="text">
<string>Download &amp;social metadata (tags/ratings/etc.) by default</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<item row="2" column="0">
<widget class="QCheckBox" name="opt_new_version_notification">
<property name="text">
<string>Show notification when &amp;new version is available</string>
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<item row="2" column="2">
<widget class="QCheckBox" name="opt_bools_are_tristate">
<property name="text">
<string>Yes/No columns have three values (Requires restart)</string>
</property>
<property name="toolTip">
<string>If checked, Yes/No custom columns values can be Yes, No, or Unknown.
If not checked, the values can be Yes or No.</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="opt_upload_news_to_device">
<property name="text">
<string>Automatically send downloaded &amp;news to ebook reader</string>
</property>
</widget>
</item>
<item row="4" column="0" colspan="2">
<item row="4" column="2">
<widget class="QCheckBox" name="opt_delete_news_from_library_on_upload">
<property name="text">
<string>&amp;Delete news from library when it is automatically sent to reader</string>
</property>
</widget>
</item>
<item row="5" column="0" colspan="2">
<layout class="QGridLayout" name="gridLayout_2">
<item row="1" column="0">
<item row="6" column="0">
<layout class="QHBoxLayout">
<item>
<widget class="QLabel" name="label_23">
<property name="text">
<string>Preferred &amp;output format:</string>
</property>
<property name="buddy">
<cstring>opt_output_format</cstring>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="opt_output_format">
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToMinimumContentsLengthWithIcon</enum>
</property>
<property name="minimumContentsLength">
<number>10</number>
</property>
</widget>
</item>
</layout>
</item>
<item row="6" column="2">
<layout class="QHBoxLayout">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Default network &amp;timeout:</string>
@ -61,7 +109,7 @@
</property>
</widget>
</item>
<item row="1" column="1">
<item>
<widget class="QSpinBox" name="opt_network_timeout">
<property name="toolTip">
<string>Set the default timeout for network fetches (i.e. anytime we go out to the internet to get information)</string>
@ -80,7 +128,21 @@
</property>
</widget>
</item>
<item row="2" column="1">
</layout>
</item>
<item row="8" column="0">
<layout class="QHBoxLayout">
<item>
<widget class="QLabel" name="priority_label">
<property name="text">
<string>Job &amp;priority:</string>
</property>
<property name="buddy">
<cstring>opt_worker_process_priority</cstring>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="opt_worker_process_priority">
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToMinimumContentsLengthWithIcon</enum>
@ -105,37 +167,11 @@
</item>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="priority_label">
<property name="text">
<string>Job &amp;priority:</string>
</property>
<property name="buddy">
<cstring>opt_worker_process_priority</cstring>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_23">
<property name="text">
<string>Preferred &amp;output format:</string>
</property>
<property name="buddy">
<cstring>opt_output_format</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="opt_output_format">
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToMinimumContentsLengthWithIcon</enum>
</property>
<property name="minimumContentsLength">
<number>10</number>
</property>
</widget>
</item>
<item row="3" column="0">
</layout>
</item>
<item row="8" column="2">
<layout class="QHBoxLayout">
<item>
<widget class="QLabel" name="label_170">
<property name="text">
<string>Restriction to apply when the current library is opened:</string>
@ -145,7 +181,7 @@
</property>
</widget>
</item>
<item row="3" column="1">
<item>
<widget class="QComboBox" name="opt_gui_restriction">
<property name="maximumSize">
<size>
@ -166,14 +202,28 @@
</item>
</layout>
</item>
<item row="6" column="0" colspan="2">
<widget class="QPushButton" name="reset_confirmation_button">
<property name="text">
<string>Reset all disabled &amp;confirmation dialogs</string>
</property>
</widget>
<item row="9" column="0">
<layout class="QHBoxLayout">
<item>
<widget class="QLabel" name="edit_metadata_single_label">
<property name="text">
<string>Edit metadata (single) layout:</string>
</property>
<property name="buddy">
<cstring>opt_edit_metadata_single_layout</cstring>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="opt_edit_metadata_single_layout">
<property name="toolTip">
<string>Choose a different layout for the Edit Metadata dialog. The compact metadata layout favors editing custom metadata over changing covers and formats.</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="7" column="0">
<item row="20" column="0">
<widget class="QGroupBox" name="groupBox_5">
<property name="title">
<string>Preferred &amp;input format order:</string>
@ -235,7 +285,7 @@
</layout>
</widget>
</item>
<item row="7" column="1">
<item row="20" column="2">
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Use internal &amp;viewer for:</string>
@ -254,6 +304,13 @@
</layout>
</widget>
</item>
<item row="30" column="0" colspan="3">
<widget class="QPushButton" name="reset_confirmation_button">
<property name="text">
<string>Reset all disabled &amp;confirmation dialogs</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources>

View File

@ -13,7 +13,6 @@ from calibre.gui2.preferences import ConfigWidgetBase, test_widget
from calibre.gui2.preferences.columns_ui import Ui_Form
from calibre.gui2.preferences.create_custom_column import CreateCustomColumn
from calibre.gui2 import error_dialog, question_dialog, ALL_COLUMNS
from calibre.utils.config import test_eight_code
class ConfigWidget(ConfigWidgetBase, Ui_Form):
@ -34,14 +33,6 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
signal = getattr(self.opt_columns, 'item'+signal)
signal.connect(self.columns_changed)
if test_eight_code:
r = self.register
choices = [(_('Default'), 'default'), (_('Compact Metadata'), 'alt1')]
r('edit_metadata_single_layout', db.prefs, choices=choices)
r('bools_are_tristate', db.prefs, restart_required=True)
else:
self.items_in_v_eight.setVisible(False)
def initialize(self):
ConfigWidgetBase.initialize(self)
self.init_columns()
@ -178,10 +169,6 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
must_restart = True
return must_restart
def refresh_gui(self, gui):
gui.library_view.reset()
if __name__ == '__main__':
from PyQt4.Qt import QApplication

View File

@ -197,67 +197,6 @@
</property>
</widget>
</item>
<item row="1" column="2">
<layout class="QVBoxLayout">
<item>
<widget class="QGroupBox" name="items_in_v_eight">
<property name="title">
<string>Related Options</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel">
<property name="text">
<string>Edit metadata layout:</string>
</property>
<property name="buddy">
<cstring>opt_edit_metadata_single_layout</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="opt_edit_metadata_single_layout">
<property name="toolTip">
<string>Choose a different layout for the Edit Metadata dialog. Alternate layouts make it easier to edit custom columns.</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel">
<property name="text">
<string>Boolean columns are tristate:</string>
</property>
<property name="buddy">
<cstring>opt_bools_are_tristate</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="opt_bools_are_tristate">
<property name="toolTip">
<string>If checked, boolean columns values can be Yes, No, and Unknown.
If not checked, the values can be Yes and No.</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_5">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
<resources>

View File

@ -80,6 +80,12 @@ class BaseModel(QAbstractListModel):
ans.append(n)
return ans
def has_action(self, name):
for a in self._data:
if a.name == name:
return True
return False
class AllModel(BaseModel):
@ -291,16 +297,13 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
def commit(self):
# Ensure preferences are showing in either the toolbar or
# the menubar.
pref_in_toolbar = lm_in_toolbar = False
cm = self.models['toolbar']
for x in cm[1]._data:
if x.name == 'Preferences':
pref_in_toolbar = True
if x.name == 'Location Manager':
lm_in_toolbar = True
if not pref_in_toolbar:
pref_in_toolbar = self.models['toolbar'][1].has_action('Preferences')
pref_in_menubar = self.models['menubar'][1].has_action('Preferences')
lm_in_toolbar = self.models['toolbar-device'][1].has_action('Location Manager')
lm_in_menubar = self.models['menubar-device'][1].has_action('Location Manager')
if not pref_in_toolbar and not pref_in_menubar:
self.models['menubar'][1].add(['Preferences'])
if not lm_in_toolbar:
if not lm_in_toolbar and not lm_in_menubar:
self.models['menubar-device'][1].add(['Location Manager'])
# Save data.

View File

@ -40,7 +40,6 @@ from calibre.ebooks import BOOK_EXTENSIONS, check_ebook_format
from calibre.utils.magick.draw import save_cover_data_to
from calibre.utils.recycle_bin import delete_file, delete_tree
from calibre.utils.formatter_functions import load_user_template_functions
from calibre.utils.config import test_eight_code
copyfile = os.link if hasattr(os, 'link') else shutil.copyfile
@ -213,11 +212,11 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
defs = self.prefs.defaults
defs['gui_restriction'] = defs['cs_restriction'] = ''
defs['categories_using_hierarchy'] = []
defs['edit_metadata_single_layout'] = 'default'
# Migrate the bool tristate tweak
defs['bools_are_tristate'] = \
tweaks.get('bool_custom_columns_are_tristate', 'yes') == 'yes'
if self.prefs.get('bools_are_tristate') is None or not test_eight_code:
if self.prefs.get('bools_are_tristate') is None:
self.prefs.set('bools_are_tristate', defs['bools_are_tristate'])
# Migrate saved search and user categories to db preference scheme

View File

@ -786,8 +786,7 @@ def write_tweaks(raw):
tweaks = read_tweaks()
test_eight_code = tweaks.get('test_eight_code', False)
# test_eight_code notes
# Change documentation of bool columns are tristate to indicate that it can be
# overridden on a per library basis via Preferences->Custom columns
# Change Amazon plugin name to just Amazon
def migrate():
if hasattr(os, 'geteuid') and os.geteuid() == 0: