Present the APNX page count method as a combobox to the user

Merge branch 'device_config' of https://github.com/user-none/calibre
This commit is contained in:
Kovid Goyal 2014-05-25 10:57:26 +05:30
commit e302d61e28
4 changed files with 94 additions and 76 deletions

View File

@ -69,7 +69,7 @@ class APNXBuilder(object):
if not pages:
pages = self.get_pages_accurate(mobi_file_path)
else:
raise('no valid accurate method chosen use fast')
raise Exception('%r is not a valid apnx generation method' % method)
except:
# Fall back to the fast parser if we can't
# use the accurate one. Typically this is

View File

@ -83,7 +83,6 @@ class KINDLE(USBMS):
'replace')
return mi
def get_annotations(self, path_map):
MBP_FORMATS = [u'azw', u'mobi', u'prc', u'txt']
mbp_formats = set(MBP_FORMATS)
@ -182,16 +181,16 @@ class KINDLE(USBMS):
spanTag['style'] = 'font-weight:bold'
if bookmark.book_format == 'pdf':
spanTag.insert(0,NavigableString(
_("%(time)s<br />Last Page Read: %(loc)d (%(pr)d%%)") % \
dict(time=strftime(u'%x', timestamp.timetuple()),
loc=last_read_location,
pr=percent_read)))
_("%(time)s<br />Last Page Read: %(loc)d (%(pr)d%%)") % dict(
time=strftime(u'%x', timestamp.timetuple()),
loc=last_read_location,
pr=percent_read)))
else:
spanTag.insert(0,NavigableString(
_("%(time)s<br />Last Page Read: Location %(loc)d (%(pr)d%%)") % \
dict(time=strftime(u'%x', timestamp.timetuple()),
loc=last_read_location,
pr=percent_read)))
_("%(time)s<br />Last Page Read: Location %(loc)d (%(pr)d%%)") % dict(
time=strftime(u'%x', timestamp.timetuple()),
loc=last_read_location,
pr=percent_read)))
divTag.insert(dtc, spanTag)
dtc += 1
@ -207,23 +206,23 @@ class KINDLE(USBMS):
for location in sorted(user_notes):
if user_notes[location]['text']:
annotations.append(
_('<b>Location %(dl)d &bull; %(typ)s</b><br />%(text)s<br />') % \
dict(dl=user_notes[location]['displayed_location'],
typ=user_notes[location]['type'],
text=(user_notes[location]['text'] if \
user_notes[location]['type'] == 'Note' else \
'<i>%s</i>' % user_notes[location]['text'])))
_('<b>Location %(dl)d &bull; %(typ)s</b><br />%(text)s<br />') % dict(
dl=user_notes[location]['displayed_location'],
typ=user_notes[location]['type'],
text=(user_notes[location]['text'] if
user_notes[location]['type'] == 'Note' else
'<i>%s</i>' % user_notes[location]['text'])))
else:
if bookmark.book_format == 'pdf':
annotations.append(
_('<b>Page %(dl)d &bull; %(typ)s</b><br />') % \
dict(dl=user_notes[location]['displayed_location'],
typ=user_notes[location]['type']))
_('<b>Page %(dl)d &bull; %(typ)s</b><br />') % dict(
dl=user_notes[location]['displayed_location'],
typ=user_notes[location]['type']))
else:
annotations.append(
_('<b>Location %(dl)d &bull; %(typ)s</b><br />') % \
dict(dl=user_notes[location]['displayed_location'],
typ=user_notes[location]['type']))
_('<b>Location %(dl)d &bull; %(typ)s</b><br />') % dict(
dl=user_notes[location]['displayed_location'],
typ=user_notes[location]['type']))
for annotation in annotations:
divTag.insert(dtc, annotation)
@ -232,7 +231,6 @@ class KINDLE(USBMS):
ka_soup.insert(0,divTag)
return ka_soup
def add_annotation_to_library(self, db, db_id, annotation):
from calibre.ebooks.BeautifulSoup import Tag
from calibre.ebooks.metadata import MetaInformation
@ -278,7 +276,7 @@ class KINDLE(USBMS):
mi.comments = last_update
db.set_metadata(mc_id[0], mi)
else:
mi = MetaInformation('My Clippings', authors = ['Kindle'])
mi = MetaInformation('My Clippings', authors=['Kindle'])
mi.tags = ['Clippings']
mi.comments = last_update
db.add_books([bm.value['path']], ['txt'], [mi])
@ -290,7 +288,9 @@ class KINDLE2(KINDLE):
FORMATS = ['azw', 'mobi', 'azw3', 'prc', 'azw1', 'tpz', 'azw4', 'pobi', 'pdf', 'txt']
DELETE_EXTS = KINDLE.DELETE_EXTS + ['.mbp1', '.mbs', '.sdr', '.han']
# On the Touch, there's also .asc files, but not using the same basename (for X-Ray & End Actions), azw3f & azw3r files, but all of them are in the .sdr sidecar folder
# On the Touch, there's also .asc files, but not using the same basename
# (for X-Ray & End Actions), azw3f & azw3r files, but all of them are in
# the .sdr sidecar folder
PRODUCT_ID = [0x0002, 0x0004]
BCD = [0x0100]
@ -298,49 +298,54 @@ class KINDLE2(KINDLE):
# SUPPORTS_SUB_DIRS_FOR_SCAN = True
EXTRA_CUSTOMIZATION_MESSAGE = [
_('Send page number information when sending books') +
':::' +
_('The Kindle 3 and newer versions can use page number information '
'in MOBI files. With this option, calibre will calculate and send'
' this information to the Kindle when uploading MOBI files by'
' USB. Note that the page numbers do not correspond to any paper'
' book.'),
_('Use slower but more accurate page number calculation') +
':::' +
_('There are two ways to generate the page number information. Using the more accurate '
'generator will produce pages that correspond better to a printed book. '
'However, this method is slower and will slow down sending files '
'to the Kindle.'),
_('Accurate calculation method') +
':::' +
_('There are multiple methods to accurately calculate the page numbers. "accurate" which '
'is an estimation based on the number of chapters, paragraphs, and visible lines in the book. '
'This method is designed to simulate an average paperback book where there are 32 lines per '
'page and a maximum of 70 characters per line. \n\n'
'The "pagebreak" method uses the presense of <mbp:pagebreak> tags within the book to '
'determine pages.'),
_('Custom column name to retrieve page counts from') +
':::' +
_('If you have a custom column in your library that you use to '
'store the page count of books, you can have calibre use that '
'information, instead of calculating a page count. Specify the '
'name of the custom column here, for example, #pages. '),
_('Send page number information when sending books') + ':::' + _(
'The Kindle 3 and newer versions can use page number information'
' in MOBI files. With this option, calibre will calculate and send'
' this information to the Kindle when uploading MOBI files by'
' USB. Note that the page numbers do not correspond to any paper'
' book.'),
_('Page count calculation method') + ':::' + '<p>' + _(
'There are multiple ways to generate the page number information.'
' If a page count is given then the book will be divided into that many pages.'
' Otherwise the number of pages will be approximated using one of the following'
' methods.<ul>'
' <li>fast: 2300 characters of uncompressed text per page.\n\n'
' <li>accurate: Based on the number of chapters, paragraphs, and visible lines in the book.'
' This method is designed to simulate an average paperback book where there are 32 lines per'
' page and a maximum of 70 characters per line.\n\n'
' <li>pagebreak: The "pagebreak" method uses the presense of <mbp:pagebreak> tags within'
' the book to determine pages.</ul>'
'Methods other than "fast" are going to be much slower.'
' Further, if "pagebreak" fails to determine a page count accurate will be used, and if '
' "accurate" fails fast will be used.'),
_('Custom column name to retrieve page counts from') + ':::' + _(
'If you have a custom column in your library that you use to'
' store the page count of books, you can have calibre use that'
' information, instead of calculating a page count. Specify the'
' name of the custom column here, for example, #pages.'),
]
EXTRA_CUSTOMIZATION_DEFAULT = [
True,
False,
'accurate',
'fast',
'',
]
OPT_APNX = 0
OPT_APNX_ACCURATE = 1
OPT_APNX_ACCURATE_METHOD = 2
OPT_APNX_CUST_COL = 3
OPT_APNX_METHOD = 1
OPT_APNX_CUST_COL = 2
EXTRA_CUSTOMIZATION_CHOICES = {OPT_APNX_METHOD:{'fast', 'accurate', 'pagebreak'}}
# x330 on the PaperWhite
THUMBNAIL_HEIGHT = 330
# x262 on the Touch. Doesn't choke on x330, though.
@classmethod
def migrate_extra_customization(cls, vals):
if isinstance(vals[cls.OPT_APNX_METHOD], bool):
# Previously this option used to be a bool
vals[cls.OPT_APNX_METHOD] = 'accurate' if vals[cls.OPT_APNX_METHOD] else 'fast'
return vals
def formats_to_scan_for(self):
ans = USBMS.formats_to_scan_for(self) | {'azw3'}
return ans
@ -408,7 +413,8 @@ class KINDLE2(KINDLE):
if not coverdata or not coverdata[2]:
return
thumb_dir = os.path.join(self._main_prefix, 'system', 'thumbnails')
if not os.path.exists(thumb_dir): return
if not os.path.exists(thumb_dir):
return
from calibre.ebooks.mobi.reader.headers import MetadataHeader
with lopen(filepath, 'rb') as f:
@ -451,12 +457,8 @@ class KINDLE2(KINDLE):
apnx_path = '%s.apnx' % os.path.join(path, filename)
apnx_builder = APNXBuilder()
try:
method = None
if opts.extra_customization[self.OPT_APNX_ACCURATE]:
method = opts.extra_customization[self.OPT_APNX_ACCURATE_METHOD]
apnx_builder.write_apnx(filepath, apnx_path,
method=method,
page_count=custom_page_count)
method = opts.extra_customization[self.OPT_APNX_METHOD]
apnx_builder.write_apnx(filepath, apnx_path, method=method, page_count=custom_page_count)
except:
print 'Failed to generate APNX'
import traceback

View File

@ -27,9 +27,13 @@ class DeviceConfig(object):
#: EXTRA_CUSTOMIZATION_MESSAGE you *must* set this as well.
EXTRA_CUSTOMIZATION_DEFAULT = None
#: A dictionary providing choices for options that should be displayed as a
#: combo box to the user. The dictionary maps extra #: customization indexes
#: to a set of choices.
EXTRA_CUSTOMIZATION_CHOICES = None
SUPPORTS_SUB_DIRS = False
SUPPORTS_SUB_DIRS_FOR_SCAN = False # This setting is used when scanning for
# books when SUPPORTS_SUB_DIRS is False
SUPPORTS_SUB_DIRS_FOR_SCAN = False # This setting is used when scanning for books when SUPPORTS_SUB_DIRS is False
SUPPORTS_SUB_DIRS_DEFAULT = True
MUST_READ_METADATA = False
@ -41,7 +45,6 @@ class DeviceConfig(object):
#: If True the user can add new formats to the driver
USER_CAN_ADD_NEW_FORMATS = True
@classmethod
def _default_save_template(cls):
from calibre.library.save_to_disk import config
@ -81,7 +84,7 @@ class DeviceConfig(object):
from calibre.gui2.device_drivers.configwidget import ConfigWidget
cw = ConfigWidget(cls.settings(), cls.FORMATS, cls.SUPPORTS_SUB_DIRS,
cls.MUST_READ_METADATA, cls.SUPPORTS_USE_AUTHOR_SORT,
cls.EXTRA_CUSTOMIZATION_MESSAGE, cls)
cls.EXTRA_CUSTOMIZATION_MESSAGE, cls, extra_customization_choices=cls.EXTRA_CUSTOMIZATION_CHOICES)
return cw
@classmethod
@ -103,6 +106,8 @@ class DeviceConfig(object):
continue
if hasattr(config_widget.opt_extra_customization[i], 'isChecked'):
ec.append(config_widget.opt_extra_customization[i].isChecked())
elif hasattr(config_widget.opt_extra_customization[i], 'currentText'):
ec.append(unicode(config_widget.opt_extra_customization[i].currentText()).strip())
else:
ec.append(unicode(config_widget.opt_extra_customization[i].text()).strip())
else:
@ -113,6 +118,10 @@ class DeviceConfig(object):
st = unicode(config_widget.opt_save_template.text())
proxy['save_template'] = st
@classmethod
def migrate_extra_customization(cls, vals):
return vals
@classmethod
def settings(cls):
opts = cls._config().parse()
@ -121,9 +130,7 @@ class DeviceConfig(object):
opts.extra_customization = []
if not isinstance(opts.extra_customization, list):
opts.extra_customization = [opts.extra_customization]
for i,d in enumerate(cls.EXTRA_CUSTOMIZATION_DEFAULT):
if i >= len(opts.extra_customization):
opts.extra_customization.append(d)
opts.extra_customization = cls.migrate_extra_customization(opts.extra_customization)
return opts
@classmethod

View File

@ -7,7 +7,7 @@ __docformat__ = 'restructuredtext en'
import textwrap
from PyQt4.Qt import (QWidget, QListWidgetItem, Qt, QVariant, QLabel,
QLineEdit, QCheckBox)
QLineEdit, QCheckBox, QComboBox)
from calibre.gui2 import error_dialog, question_dialog
from calibre.gui2.device_drivers.configwidget_ui import Ui_ConfigWidget
@ -18,7 +18,7 @@ class ConfigWidget(QWidget, Ui_ConfigWidget):
def __init__(self, settings, all_formats, supports_subdirs,
must_read_metadata, supports_use_author_sort,
extra_customization_message, device):
extra_customization_message, device, extra_customization_choices=None):
QWidget.__init__(self)
Ui_ConfigWidget.__init__(self)
@ -62,6 +62,7 @@ class ConfigWidget(QWidget, Ui_ConfigWidget):
else:
self.opt_use_author_sort.hide()
if extra_customization_message:
extra_customization_choices = extra_customization_choices or {}
def parse_msg(m):
msg, _, tt = m.partition(':::') if m else ('', '', '')
return msg.strip(), textwrap.fill(tt.strip(), 100)
@ -84,6 +85,14 @@ class ConfigWidget(QWidget, Ui_ConfigWidget):
self.opt_extra_customization.append(QCheckBox(label_text))
self.opt_extra_customization[-1].setToolTip(tt)
self.opt_extra_customization[i].setChecked(bool(settings.extra_customization[i]))
elif i in extra_customization_choices:
cb = QComboBox(self)
self.opt_extra_customization.append(cb)
l = QLabel(label_text)
l.setToolTip(tt), cb.setToolTip(tt), l.setBuddy(cb), cb.setToolTip(tt)
for li in sorted(extra_customization_choices[i]):
self.opt_extra_customization[i].addItem(li)
cb.setCurrentIndex(max(0, cb.findText(settings.extra_customization[i])))
else:
self.opt_extra_customization.append(QLineEdit(self))
l = QLabel(label_text)
@ -111,7 +120,6 @@ class ConfigWidget(QWidget, Ui_ConfigWidget):
self.extra_layout.addWidget(self.opt_extra_customization, 1, 0)
self.opt_save_template.setText(settings.save_template)
def up_column(self):
idx = self.columns.currentRow()
if idx > 0:
@ -125,7 +133,8 @@ class ConfigWidget(QWidget, Ui_ConfigWidget):
self.columns.setCurrentRow(idx+1)
def format_map(self):
formats = [unicode(self.columns.item(i).data(Qt.UserRole).toString()) for i in range(self.columns.count()) if self.columns.item(i).checkState()==Qt.Checked]
formats = [unicode(self.columns.item(i).data(Qt.UserRole).toString())
for i in range(self.columns.count()) if self.columns.item(i).checkState()==Qt.Checked]
return formats
def use_subdirs(self):
@ -156,7 +165,7 @@ class ConfigWidget(QWidget, Ui_ConfigWidget):
return True
except Exception as err:
error_dialog(self, _('Invalid template'),
'<p>'+_('The template %s is invalid:')%tmpl + \
'<p>'+_('The template %s is invalid:')%tmpl +
'<br>'+unicode(err), show=True)
return False