Merge from trunk

This commit is contained in:
Sengian 2010-12-13 09:02:47 +01:00
commit c6fab3c849
25 changed files with 437 additions and 77 deletions

View File

@ -0,0 +1,23 @@
__license__ = 'GPL v3'
__copyright__ = '2010, Hiroshi Miura <miurahr@linux.com>'
'''
ajiajin.com/blog
'''
from calibre.web.feeds.news import BasicNewsRecipe
class AjiajinBlog(BasicNewsRecipe):
title = u'Ajiajin blog'
__author__ = 'Hiroshi Miura'
oldest_article = 5
publication_type = 'blog'
max_articles_per_feed = 100
description = 'The next generation internet trends in Japan and Asia'
publisher = ''
category = 'internet, asia, japan'
language = 'en'
encoding = 'utf-8'
feeds = [(u'blog', u'http://feeds.feedburner.com/Asiajin')]

View File

@ -0,0 +1,37 @@
__license__ = 'GPL v3'
__copyright__ = '2010, Hiroshi Miura <miurahr@linux.com>'
'''
http://ameblo.jp/
'''
import re
from calibre.web.feeds.news import BasicNewsRecipe
class SakuraBlog(BasicNewsRecipe):
title = u'chou chou blog'
__author__ = 'Hiroshi Miura'
oldest_article = 4
publication_type = 'blog'
max_articles_per_feed = 20
description = 'Japanese popular dog blog'
publisher = ''
category = 'dog, pet, japan'
language = 'ja'
encoding = 'utf-8'
use_embedded_content = True
feeds = [(u'blog', u'http://feedblog.ameba.jp/rss/ameblo/chouchou1218/rss20.xml')]
def parse_feeds(self):
feeds = BasicNewsRecipe.parse_feeds(self)
for curfeed in feeds:
delList = []
for a,curarticle in enumerate(curfeed.articles):
if re.search(r'rssad.jp', curarticle.url):
delList.append(curarticle)
if len(delList)>0:
for d in delList:
index = curfeed.articles.index(d)
curfeed.articles[index:index+1] = []
return feeds

View File

@ -3,15 +3,16 @@ __copyright__ = '2009, Darko Miletic <darko.miletic at gmail.com>'
''' '''
http://www.dilbert.com http://www.dilbert.com
''' '''
import re
from calibre.web.feeds.recipes import BasicNewsRecipe from calibre.web.feeds.recipes import BasicNewsRecipe
import re
class DosisDiarias(BasicNewsRecipe): class DilbertBig(BasicNewsRecipe):
title = 'Dilbert' title = 'Dilbert'
__author__ = 'Darko Miletic' __author__ = 'Darko Miletic and Starson17'
description = 'Dilbert' description = 'Dilbert'
oldest_article = 5 reverse_article_order = True
oldest_article = 15
max_articles_per_feed = 100 max_articles_per_feed = 100
no_stylesheets = True no_stylesheets = True
use_embedded_content = True use_embedded_content = True
@ -29,20 +30,23 @@ class DosisDiarias(BasicNewsRecipe):
feeds = [(u'Dilbert', u'http://feeds.dilbert.com/DilbertDailyStrip' )] feeds = [(u'Dilbert', u'http://feeds.dilbert.com/DilbertDailyStrip' )]
preprocess_regexps = [
(re.compile('strip\..*\.gif', re.DOTALL|re.IGNORECASE),
lambda match: 'strip.zoom.gif')
]
def get_article_url(self, article): def get_article_url(self, article):
return article.get('feedburner_origlink', None) return article.get('feedburner_origlink', None)
preprocess_regexps = [
(re.compile('strip\..*\.gif', re.DOTALL|re.IGNORECASE), lambda match: 'strip.zoom.gif')
]
def preprocess_html(self, soup): def preprocess_html(self, soup):
for tag in soup.findAll(name='a'): for tag in soup.findAll(name='a'):
if tag['href'].find('http://feedads') >= 0: if tag['href'].find('http://feedads') >= 0:
tag.extract() tag.extract()
return soup return soup
extra_css = '''
h1{font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:large;}
h2{font-family:Arial,Helvetica,sans-serif; font-weight:normal;font-size:small;}
img {max-width:100%; min-width:100%;}
p{font-family:Arial,Helvetica,sans-serif;font-size:small;}
body{font-family:Helvetica,Arial,sans-serif;font-size:small;}
'''

View File

@ -0,0 +1,31 @@
__license__ = 'GPL v3'
__copyright__ = '2010, Hiroshi Miura <miurahr@linux.com>'
'''
www.kahoku.co.jp
'''
from calibre.web.feeds.news import BasicNewsRecipe
class KahokuShinpoNews(BasicNewsRecipe):
title = u'\u6cb3\u5317\u65b0\u5831'
__author__ = 'Hiroshi Miura'
oldest_article = 2
max_articles_per_feed = 20
description = 'Tohoku regional news paper in Japan'
publisher = 'Kahoku Shinpo Sha'
category = 'news, japan'
language = 'ja'
encoding = 'Shift_JIS'
no_stylesheets = True
feeds = [(u'news', u'http://www.kahoku.co.jp/rss/index_thk.xml')]
keep_only_tags = [ dict(id="page_title"),
dict(id="news_detail"),
dict(id="bt_title"),
{'class':"photoLeft"},
dict(id="bt_body")
]
remove_tags = [ {'class':"button"}]

View File

@ -0,0 +1,38 @@
__license__ = 'GPL v3'
__copyright__ = '2010, Hiroshi Miura <miurahr@linux.com>'
'''
nationalgeographic.com
'''
from calibre.web.feeds.news import BasicNewsRecipe
import re
class NationalGeographicNews(BasicNewsRecipe):
title = u'National Geographic News'
oldest_article = 7
max_articles_per_feed = 100
remove_javascript = True
no_stylesheets = True
use_embedded_content = False
feeds = [(u'news', u'http://feeds.nationalgeographic.com/ng/News/News_Main')]
remove_tags_before = dict(id='page_head')
remove_tags_after = [dict(id='social_buttons'),{'class':'aside'}]
remove_tags = [
{'class':'hidden'}
]
def parse_feeds(self):
feeds = BasicNewsRecipe.parse_feeds(self)
for curfeed in feeds:
delList = []
for a,curarticle in enumerate(curfeed.articles):
if re.search(r'ads\.pheedo\.com', curarticle.url):
delList.append(curarticle)
if len(delList)>0:
for d in delList:
index = curfeed.articles.index(d)
curfeed.articles[index:index+1] = []
return feeds

View File

@ -0,0 +1,20 @@
__license__ = 'GPL v3'
__copyright__ = '2010, Hiroshi Miura <miurahr@linux.com>'
'''
nationalgeographic.co.jp
'''
from calibre.web.feeds.news import BasicNewsRecipe
import re
class NationalGeoJp(BasicNewsRecipe):
title = u'\u30ca\u30b7\u30e7\u30ca\u30eb\u30fb\u30b8\u30aa\u30b0\u30e9\u30d5\u30a3\u30c3\u30af\u30cb\u30e5\u30fc\u30b9'
oldest_article = 7
max_articles_per_feed = 100
no_stylesheets = True
feeds = [(u'news', u'http://www.nationalgeographic.co.jp/news/rss.php')]
def print_version(self, url):
return re.sub(r'news_article.php','news_printer_friendly.php', url)

View File

@ -10,8 +10,8 @@ import mechanize
from calibre.ptempfile import PersistentTemporaryFile from calibre.ptempfile import PersistentTemporaryFile
class NikkeiNet_sub_life(BasicNewsRecipe): class NikkeiNet_sub_shakai(BasicNewsRecipe):
title = u'\u65e5\u7d4c\u65b0\u805e\u96fb\u5b50\u7248(\u751f\u6d3b)' title = u'\u65e5\u7d4c\u65b0\u805e\u96fb\u5b50\u7248(Social)'
__author__ = 'Hiroshi Miura' __author__ = 'Hiroshi Miura'
description = 'News and current market affairs from Japan' description = 'News and current market affairs from Japan'
cover_url = 'http://parts.nikkei.com/parts/ds/images/common/logo_r1.svg' cover_url = 'http://parts.nikkei.com/parts/ds/images/common/logo_r1.svg'

View File

@ -0,0 +1,58 @@
__license__ = 'GPL v3'
__copyright__ = '2010, Hiroshi Miura <miurahr@linux.com>'
'''
paperli
'''
from calibre.web.feeds.news import BasicNewsRecipe
from calibre import strftime
class paperli_topics(BasicNewsRecipe):
# Customize this recipe and change paperli_tag and title below to
# download news on your favorite tag
paperli_tag = 'climate'
title = u'The #climate Daily - paperli'
#-------------------------------------------------------------
__author__ = 'Hiroshi Miura'
oldest_article = 7
max_articles_per_feed = 100
description = 'paper.li page about '+ paperli_tag
publisher = 'paper.li'
category = 'paper.li'
language = 'en'
encoding = 'utf-8'
remove_javascript = True
masthead_title = u'The '+ paperli_tag +' Daily'
timefmt = '[%y/%m/%d]'
base_url = 'http://paper.li'
index = base_url+'/tag/'+paperli_tag
def parse_index(self):
# get topics
topics = []
soup = self.index_to_soup(self.index)
topics_lists = soup.find('div',attrs={'class':'paper-nav-bottom'})
for item in topics_lists.findAll('li', attrs={'class':""}):
itema = item.find('a',href=True)
topics.append({'title': itema.string, 'url': itema['href']})
#get feeds
feeds = []
for topic in topics:
newsarticles = []
soup = self.index_to_soup(''.join([self.base_url, topic['url'] ]))
topstories = soup.findAll('div',attrs={'class':'yui-u'})
for itt in topstories:
itema = itt.find('a',href=True,attrs={'class':'ts'})
if itema is not None:
itemd = itt.find('div',text=True, attrs={'class':'text'})
newsarticles.append({
'title' :itema.string
,'date' :strftime(self.timefmt)
,'url' :itema['href']
,'description':itemd.string
})
feeds.append((topic['title'], newsarticles))
return feeds

View File

@ -0,0 +1,36 @@
__license__ = 'GPL v3'
__copyright__ = '2010, Hiroshi Miura <miurahr@linux.com>'
'''
http://ameblo.jp/sauta19/
'''
import re
from calibre.web.feeds.news import BasicNewsRecipe
class UniNoHimituKichiBlog(BasicNewsRecipe):
title = u'Uni secret base'
__author__ = 'Hiroshi Miura'
oldest_article = 2
publication_type = 'blog'
max_articles_per_feed = 20
description = 'Japanese famous Cat blog'
publisher = ''
category = 'cat, pet, japan'
language = 'ja'
encoding = 'utf-8'
feeds = [(u'blog', u'http://feedblog.ameba.jp/rss/ameblo/sauta19/rss20.xml')]
def parse_feeds(self):
feeds = BasicNewsRecipe.parse_feeds(self)
for curfeed in feeds:
delList = []
for a,curarticle in enumerate(curfeed.articles):
if re.search(r'rssad.jp', curarticle.url):
delList.append(curarticle)
if len(delList)>0:
for d in delList:
index = curfeed.articles.index(d)
curfeed.articles[index:index+1] = []
return feeds

View File

@ -120,7 +120,7 @@ def add_pipeline_options(parser, plumber):
[ [
'base_font_size', 'disable_font_rescaling', 'base_font_size', 'disable_font_rescaling',
'font_size_mapping', 'font_size_mapping',
'line_height', 'line_height', 'minimum_line_height',
'linearize_tables', 'linearize_tables',
'extra_css', 'smarten_punctuation', 'extra_css', 'smarten_punctuation',
'margin_top', 'margin_left', 'margin_right', 'margin_top', 'margin_left', 'margin_right',

View File

@ -160,13 +160,30 @@ OptionRecommendation(name='disable_font_rescaling',
) )
), ),
OptionRecommendation(name='minimum_line_height',
recommended_value=120.0, level=OptionRecommendation.LOW,
help=_(
'The minimum line height, as a percentage of the element\'s '
'calculated font size. calibre will ensure that every element '
'has a line height of at least this setting, irrespective of '
'what the input document specifies. Set to zero to disable. '
'Default is 120%. Use this setting in preference to '
'the direct line height specification, unless you know what '
'you are doing. For example, you can achieve "double spaced" '
'text by setting this to 240.'
)
),
OptionRecommendation(name='line_height', OptionRecommendation(name='line_height',
recommended_value=0, level=OptionRecommendation.LOW, recommended_value=0, level=OptionRecommendation.LOW,
help=_('The line height in pts. Controls spacing between consecutive ' help=_(
'lines of text. By default no line height manipulation is ' 'The line height in pts. Controls spacing between consecutive '
'performed.' 'lines of text. Only applies to elements that do not define '
) 'their own line height. In most cases, the minimum line height '
'option is more useful. '
'By default no line height manipulation is performed.'
)
), ),
OptionRecommendation(name='linearize_tables', OptionRecommendation(name='linearize_tables',

View File

@ -314,6 +314,8 @@ class HTMLInput(InputFormatPlugin):
rewrite_links, urlnormalize, urldefrag, BINARY_MIME, OEB_STYLES, \ rewrite_links, urlnormalize, urldefrag, BINARY_MIME, OEB_STYLES, \
xpath xpath
from calibre import guess_type from calibre import guess_type
from calibre.ebooks.oeb.transforms.metadata import \
meta_info_to_oeb_metadata
import cssutils import cssutils
self.OEB_STYLES = OEB_STYLES self.OEB_STYLES = OEB_STYLES
oeb = create_oebbook(log, None, opts, self, oeb = create_oebbook(log, None, opts, self,
@ -321,15 +323,7 @@ class HTMLInput(InputFormatPlugin):
self.oeb = oeb self.oeb = oeb
metadata = oeb.metadata metadata = oeb.metadata
if mi.title: meta_info_to_oeb_metadata(mi, metadata, log)
metadata.add('title', mi.title)
if mi.authors:
for a in mi.authors:
metadata.add('creator', a, attrib={'role':'aut'})
if mi.publisher:
metadata.add('publisher', mi.publisher)
if mi.isbn:
metadata.add('identifier', mi.isbn, attrib={'scheme':'ISBN'})
if not metadata.language: if not metadata.language:
oeb.logger.warn(u'Language not specified') oeb.logger.warn(u'Language not specified')
metadata.add('language', get_lang().replace('_', '-')) metadata.add('language', get_lang().replace('_', '-'))

View File

@ -170,7 +170,27 @@ def get_metadata_(src, encoding=None):
if match: if match:
series = match.group(1) series = match.group(1)
if series: if series:
pat = re.compile(r'\[([.0-9]+)\]')
match = pat.search(series)
series_index = None
if match is not None:
try:
series_index = float(match.group(1))
except:
pass
series = series.replace(match.group(), '').strip()
mi.series = ent_pat.sub(entity_to_unicode, series) mi.series = ent_pat.sub(entity_to_unicode, series)
if series_index is None:
pat = get_meta_regexp_("Seriesnumber")
match = pat.search(src)
if match:
try:
series_index = float(match.group(1))
except:
pass
if series_index is not None:
mi.series_index = series_index
# RATING # RATING
rating = None rating = None

View File

@ -184,7 +184,7 @@ class MobiMLizer(object):
para.attrib['value'] = str(istates[-2].list_num) para.attrib['value'] = str(istates[-2].list_num)
elif tag in NESTABLE_TAGS and istate.rendered: elif tag in NESTABLE_TAGS and istate.rendered:
para = wrapper = bstate.nested[-1] para = wrapper = bstate.nested[-1]
elif left > 0 and indent >= 0: elif not self.opts.mobi_ignore_margins and left > 0 and indent >= 0:
ems = self.profile.mobi_ems_per_blockquote ems = self.profile.mobi_ems_per_blockquote
para = wrapper = etree.SubElement(parent, XHTML('blockquote')) para = wrapper = etree.SubElement(parent, XHTML('blockquote'))
para = wrapper para = wrapper

View File

@ -39,6 +39,12 @@ class MOBIOutput(OutputFormatPlugin):
OptionRecommendation(name='personal_doc', recommended_value='[PDOC]', OptionRecommendation(name='personal_doc', recommended_value='[PDOC]',
help=_('Tag marking book to be filed with Personal Docs') help=_('Tag marking book to be filed with Personal Docs')
), ),
OptionRecommendation(name='mobi_ignore_margins',
recommended_value=False,
help=_('Ignore margins in the input document. If False, then '
'the MOBI output plugin will try to convert margins specified'
' in the input document, otherwise it will ignore them.')
),
]) ])
def check_for_periodical(self): def check_for_periodical(self):

View File

@ -633,12 +633,12 @@ class Style(object):
parent = self._getparent() parent = self._getparent()
if 'line-height' in self._style: if 'line-height' in self._style:
lineh = self._style['line-height'] lineh = self._style['line-height']
if lineh == 'normal':
lineh = '1.2'
try: try:
float(lineh) result = float(lineh) * self.fontSize
except ValueError: except ValueError:
result = self._unit_convert(lineh, base=self.fontSize) result = self._unit_convert(lineh, base=self.fontSize)
else:
result = float(lineh) * self.fontSize
elif parent is not None: elif parent is not None:
# TODO: proper inheritance # TODO: proper inheritance
result = parent.lineHeight result = parent.lineHeight

View File

@ -245,6 +245,8 @@ class CSSFlattener(object):
del node.attrib['bgcolor'] del node.attrib['bgcolor']
if cssdict.get('font-weight', '').lower() == 'medium': if cssdict.get('font-weight', '').lower() == 'medium':
cssdict['font-weight'] = 'normal' # ADE chokes on font-weight medium cssdict['font-weight'] = 'normal' # ADE chokes on font-weight medium
fsize = font_size
if not self.context.disable_font_rescaling: if not self.context.disable_font_rescaling:
_sbase = self.sbase if self.sbase is not None else \ _sbase = self.sbase if self.sbase is not None else \
self.context.source.fbase self.context.source.fbase
@ -258,6 +260,14 @@ class CSSFlattener(object):
fsize = self.fmap[font_size] fsize = self.fmap[font_size]
cssdict['font-size'] = "%0.5fem" % (fsize / psize) cssdict['font-size'] = "%0.5fem" % (fsize / psize)
psize = fsize psize = fsize
try:
minlh = self.context.minimum_line_height / 100.
if style['line-height'] < minlh * fsize:
cssdict['line-height'] = str(minlh)
except:
self.oeb.logger.exception('Failed to set minimum line-height')
if cssdict: if cssdict:
if self.lineh and self.fbase and tag != 'body': if self.lineh and self.fbase and tag != 'body':
self.clean_edges(cssdict, style, psize) self.clean_edges(cssdict, style, psize)
@ -290,6 +300,7 @@ class CSSFlattener(object):
lineh = self.lineh / psize lineh = self.lineh / psize
cssdict['line-height'] = "%0.5fem" % lineh cssdict['line-height'] = "%0.5fem" % lineh
if (self.context.remove_paragraph_spacing or if (self.context.remove_paragraph_spacing or
self.context.insert_blank_line) and tag in ('p', 'div'): self.context.insert_blank_line) and tag in ('p', 'div'):
if item_id != 'calibre_jacket' or self.context.output_profile.name == 'Kindle': if item_id != 'calibre_jacket' or self.context.output_profile.name == 'Kindle':

View File

@ -77,10 +77,14 @@ class TXTInput(InputFormatPlugin):
base = os.getcwdu() base = os.getcwdu()
if hasattr(stream, 'name'): if hasattr(stream, 'name'):
base = os.path.dirname(stream.name) base = os.path.dirname(stream.name)
htmlfile = open(os.path.join(base, 'temp_calibre_txt_input_to_html.html'), fname = os.path.join(base, 'index.html')
'wb') c = 0
htmlfile.write(html.encode('utf-8')) while os.path.exists(fname):
htmlfile.close() c += 1
fname = 'index%d.html'%c
htmlfile = open(fname, 'wb')
with htmlfile:
htmlfile.write(html.encode('utf-8'))
cwd = os.getcwdu() cwd = os.getcwdu()
odi = options.debug_pipeline odi = options.debug_pipeline
options.debug_pipeline = None options.debug_pipeline = None

View File

@ -10,7 +10,7 @@ import textwrap
from functools import partial from functools import partial
from PyQt4.Qt import QWidget, QSpinBox, QDoubleSpinBox, QLineEdit, QTextEdit, \ from PyQt4.Qt import QWidget, QSpinBox, QDoubleSpinBox, QLineEdit, QTextEdit, \
QCheckBox, QComboBox, Qt, QIcon, pyqtSignal QCheckBox, QComboBox, Qt, QIcon, pyqtSignal, QLabel
from calibre.customize.conversion import OptionRecommendation from calibre.customize.conversion import OptionRecommendation
from calibre.ebooks.conversion.config import load_defaults, \ from calibre.ebooks.conversion.config import load_defaults, \
@ -81,6 +81,21 @@ class Widget(QWidget):
self.apply_recommendations(defaults) self.apply_recommendations(defaults)
self.setup_help(get_help) self.setup_help(get_help)
def process_child(child):
for g in child.children():
if isinstance(g, QLabel):
buddy = g.buddy()
if buddy is not None and hasattr(buddy, '_help'):
g._help = buddy._help
htext = unicode(buddy.toolTip()).strip()
g.setToolTip(htext)
g.setWhatsThis(htext)
g.__class__.enterEvent = lambda obj, event: self.set_help(getattr(obj, '_help', obj.toolTip()))
else:
process_child(g)
process_child(self)
def restore_defaults(self, get_option): def restore_defaults(self, get_option):
defaults = GuiRecommendations() defaults = GuiRecommendations()
defaults.merge_recommendations(get_option, OptionRecommendation.LOW, defaults.merge_recommendations(get_option, OptionRecommendation.LOW,

View File

@ -21,7 +21,7 @@ class LookAndFeelWidget(Widget, Ui_Form):
def __init__(self, parent, get_option, get_help, db=None, book_id=None): def __init__(self, parent, get_option, get_help, db=None, book_id=None):
Widget.__init__(self, parent, Widget.__init__(self, parent,
['change_justification', 'extra_css', 'base_font_size', ['change_justification', 'extra_css', 'base_font_size',
'font_size_mapping', 'line_height', 'font_size_mapping', 'line_height', 'minimum_line_height',
'linearize_tables', 'smarten_punctuation', 'linearize_tables', 'smarten_punctuation',
'disable_font_rescaling', 'insert_blank_line', 'disable_font_rescaling', 'insert_blank_line',
'remove_paragraph_spacing', 'remove_paragraph_spacing_indent_size','input_encoding', 'remove_paragraph_spacing', 'remove_paragraph_spacing_indent_size','input_encoding',

View File

@ -97,7 +97,7 @@
</item> </item>
</layout> </layout>
</item> </item>
<item row="3" column="0"> <item row="4" column="0">
<widget class="QLabel" name="label"> <widget class="QLabel" name="label">
<property name="text"> <property name="text">
<string>Line &amp;height:</string> <string>Line &amp;height:</string>
@ -107,7 +107,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="1" colspan="2"> <item row="4" column="1" colspan="2">
<widget class="QDoubleSpinBox" name="opt_line_height"> <widget class="QDoubleSpinBox" name="opt_line_height">
<property name="suffix"> <property name="suffix">
<string> pt</string> <string> pt</string>
@ -117,7 +117,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="0"> <item row="5" column="0">
<widget class="QLabel" name="label_3"> <widget class="QLabel" name="label_3">
<property name="text"> <property name="text">
<string>Input character &amp;encoding:</string> <string>Input character &amp;encoding:</string>
@ -127,17 +127,17 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="1" colspan="3"> <item row="5" column="1" colspan="3">
<widget class="QLineEdit" name="opt_input_encoding"/> <widget class="QLineEdit" name="opt_input_encoding"/>
</item> </item>
<item row="5" column="0" colspan="2"> <item row="6" column="0" colspan="2">
<widget class="QCheckBox" name="opt_remove_paragraph_spacing"> <widget class="QCheckBox" name="opt_remove_paragraph_spacing">
<property name="text"> <property name="text">
<string>Remove &amp;spacing between paragraphs</string> <string>Remove &amp;spacing between paragraphs</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="2" colspan="2"> <item row="6" column="2" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_2"> <layout class="QHBoxLayout" name="horizontalLayout_2">
<item> <item>
<widget class="QLabel" name="label_4"> <widget class="QLabel" name="label_4">
@ -164,21 +164,21 @@
</item> </item>
</layout> </layout>
</item> </item>
<item row="6" column="0"> <item row="7" column="0">
<widget class="QLabel" name="label_5"> <widget class="QLabel" name="label_5">
<property name="text"> <property name="text">
<string>Text justification:</string> <string>Text justification:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="7" column="0"> <item row="8" column="0">
<widget class="QCheckBox" name="opt_linearize_tables"> <widget class="QCheckBox" name="opt_linearize_tables">
<property name="text"> <property name="text">
<string>&amp;Linearize tables</string> <string>&amp;Linearize tables</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="10" column="0" colspan="4"> <item row="11" column="0" colspan="4">
<widget class="QGroupBox" name="groupBox"> <widget class="QGroupBox" name="groupBox">
<property name="title"> <property name="title">
<string>Extra &amp;CSS</string> <string>Extra &amp;CSS</string>
@ -190,37 +190,60 @@
</layout> </layout>
</widget> </widget>
</item> </item>
<item row="6" column="2" colspan="2"> <item row="7" column="2" colspan="2">
<widget class="QComboBox" name="opt_change_justification"/> <widget class="QComboBox" name="opt_change_justification"/>
</item> </item>
<item row="7" column="1" colspan="3"> <item row="8" column="1" colspan="3">
<widget class="QCheckBox" name="opt_asciiize"> <widget class="QCheckBox" name="opt_asciiize">
<property name="text"> <property name="text">
<string>&amp;Transliterate unicode characters to ASCII</string> <string>&amp;Transliterate unicode characters to ASCII</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="8" column="0"> <item row="9" column="0">
<widget class="QCheckBox" name="opt_insert_blank_line"> <widget class="QCheckBox" name="opt_insert_blank_line">
<property name="text"> <property name="text">
<string>Insert &amp;blank line</string> <string>Insert &amp;blank line</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="8" column="1" colspan="2"> <item row="9" column="1" colspan="2">
<widget class="QCheckBox" name="opt_keep_ligatures"> <widget class="QCheckBox" name="opt_keep_ligatures">
<property name="text"> <property name="text">
<string>Keep &amp;ligatures</string> <string>Keep &amp;ligatures</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="9" column="0"> <item row="10" column="0">
<widget class="QCheckBox" name="opt_smarten_punctuation"> <widget class="QCheckBox" name="opt_smarten_punctuation">
<property name="text"> <property name="text">
<string>Smarten &amp;punctuation</string> <string>Smarten &amp;punctuation</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Minimum &amp;line height:</string>
</property>
<property name="buddy">
<cstring>opt_minimum_line_height</cstring>
</property>
</widget>
</item>
<item row="3" column="1" colspan="2">
<widget class="QDoubleSpinBox" name="opt_minimum_line_height">
<property name="suffix">
<string> %</string>
</property>
<property name="decimals">
<number>1</number>
</property>
<property name="maximum">
<double>900.000000000000000</double>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<resources> <resources>

View File

@ -25,6 +25,7 @@ class PluginWidget(Widget, Ui_Form):
def __init__(self, parent, get_option, get_help, db=None, book_id=None): def __init__(self, parent, get_option, get_help, db=None, book_id=None):
Widget.__init__(self, parent, Widget.__init__(self, parent,
['prefer_author_sort', 'rescale_images', 'toc_title', ['prefer_author_sort', 'rescale_images', 'toc_title',
'mobi_ignore_margins',
'dont_compress', 'no_inline_toc', 'masthead_font','personal_doc'] 'dont_compress', 'no_inline_toc', 'masthead_font','personal_doc']
) )
self.db, self.book_id = db, book_id self.db, self.book_id = db, book_id

View File

@ -55,7 +55,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="0" colspan="2"> <item row="6" column="0" colspan="2">
<widget class="QGroupBox" name="groupBox"> <widget class="QGroupBox" name="groupBox">
<property name="title"> <property name="title">
<string>Kindle options</string> <string>Kindle options</string>
@ -101,7 +101,7 @@
</layout> </layout>
</widget> </widget>
</item> </item>
<item row="6" column="0"> <item row="7" column="0">
<spacer name="verticalSpacer_2"> <spacer name="verticalSpacer_2">
<property name="orientation"> <property name="orientation">
<enum>Qt::Vertical</enum> <enum>Qt::Vertical</enum>
@ -114,6 +114,13 @@
</property> </property>
</spacer> </spacer>
</item> </item>
<item row="5" column="0">
<widget class="QCheckBox" name="opt_mobi_ignore_margins">
<property name="text">
<string>Ignore &amp;margins</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<resources/> <resources/>

View File

@ -240,37 +240,39 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
self.cover_fetcher = CoverFetcher(None, None, isbn, self.cover_fetcher = CoverFetcher(None, None, isbn,
self.timeout, title, author) self.timeout, title, author)
self.cover_fetcher.start() self.cover_fetcher.start()
self._hangcheck = QTimer(self)
self._hangcheck.timeout.connect(self.hangcheck,
type=Qt.QueuedConnection)
self.cf_start_time = time.time() self.cf_start_time = time.time()
self.pi.start(_('Downloading cover...')) self.pi.start(_('Downloading cover...'))
self._hangcheck.start(100) QTimer.singleShot(100, self.hangcheck)
def hangcheck(self): def hangcheck(self):
if self.cover_fetcher.is_alive() and \ cf = self.cover_fetcher
time.time()-self.cf_start_time < self.COVER_FETCH_TIMEOUT: if cf is None:
# Called after dialog closed
return
if cf.is_alive() and \
time.time()-self.cf_start_time < self.COVER_FETCH_TIMEOUT:
QTimer.singleShot(100, self.hangcheck)
return return
self._hangcheck.stop()
try: try:
if self.cover_fetcher.is_alive(): if cf.is_alive():
error_dialog(self, _('Cannot fetch cover'), error_dialog(self, _('Cannot fetch cover'),
_('<b>Could not fetch cover.</b><br/>')+ _('<b>Could not fetch cover.</b><br/>')+
_('The download timed out.')).exec_() _('The download timed out.')).exec_()
return return
if self.cover_fetcher.needs_isbn: if cf.needs_isbn:
error_dialog(self, _('Cannot fetch cover'), error_dialog(self, _('Cannot fetch cover'),
_('Could not find cover for this book. Try ' _('Could not find cover for this book. Try '
'specifying the ISBN first.')).exec_() 'specifying the ISBN first.')).exec_()
return return
if self.cover_fetcher.exception is not None: if cf.exception is not None:
err = self.cover_fetcher.exception err = cf.exception
error_dialog(self, _('Cannot fetch cover'), error_dialog(self, _('Cannot fetch cover'),
_('<b>Could not fetch cover.</b><br/>')+unicode(err)).exec_() _('<b>Could not fetch cover.</b><br/>')+unicode(err)).exec_()
return return
if self.cover_fetcher.errors and self.cover_fetcher.cover_data is None: if cf.errors and cf.cover_data is None:
details = u'\n\n'.join([e[-1] + ': ' + e[1] for e in self.cover_fetcher.errors]) details = u'\n\n'.join([e[-1] + ': ' + e[1] for e in cf.errors])
error_dialog(self, _('Cannot fetch cover'), error_dialog(self, _('Cannot fetch cover'),
_('<b>Could not fetch cover.</b><br/>') + _('<b>Could not fetch cover.</b><br/>') +
_('For the error message from each cover source, ' _('For the error message from each cover source, '
@ -278,7 +280,7 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
return return
pix = QPixmap() pix = QPixmap()
pix.loadFromData(self.cover_fetcher.cover_data) pix.loadFromData(cf.cover_data)
if pix.isNull(): if pix.isNull():
error_dialog(self, _('Bad cover'), error_dialog(self, _('Bad cover'),
_('The cover is not a valid picture')).exec_() _('The cover is not a valid picture')).exec_()
@ -287,7 +289,7 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
self.update_cover_tooltip() self.update_cover_tooltip()
self.cover_changed = True self.cover_changed = True
self.cpixmap = pix self.cpixmap = pix
self.cover_data = self.cover_fetcher.cover_data self.cover_data = cf.cover_data
finally: finally:
self.fetch_cover_button.setEnabled(True) self.fetch_cover_button.setEnabled(True)
self.unsetCursor() self.unsetCursor()
@ -438,6 +440,7 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
def __init__(self, window, row, db, prev=None, def __init__(self, window, row, db, prev=None,
next_=None): next_=None):
ResizableDialog.__init__(self, window) ResizableDialog.__init__(self, window)
self.cover_fetcher = None
self.bc_box.layout().setAlignment(self.cover, Qt.AlignCenter|Qt.AlignHCenter) self.bc_box.layout().setAlignment(self.cover, Qt.AlignCenter|Qt.AlignHCenter)
self.cancel_all = False self.cancel_all = False
base = unicode(self.author_sort.toolTip()) base = unicode(self.author_sort.toolTip())
@ -828,10 +831,6 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
self.accept() self.accept()
def accept(self): def accept(self):
cf = getattr(self, 'cover_fetcher', None)
if cf is not None and hasattr(cf, 'terminate'):
cf.terminate()
cf.wait()
try: try:
if self.formats_changed: if self.formats_changed:
self.sync_formats() self.sync_formats()
@ -888,14 +887,12 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
show=True) show=True)
raise raise
self.save_state() self.save_state()
self.cover_fetcher = None
QDialog.accept(self) QDialog.accept(self)
def reject(self, *args): def reject(self, *args):
cf = getattr(self, 'cover_fetcher', None)
if cf is not None and hasattr(cf, 'terminate'):
cf.terminate()
cf.wait()
self.save_state() self.save_state()
self.cover_fetcher = None
QDialog.reject(self, *args) QDialog.reject(self, *args)
def read_state(self): def read_state(self):

View File

@ -251,10 +251,28 @@ class Preferences(QMainWindow):
self.close() self.close()
self.run_wizard_requested.emit() self.run_wizard_requested.emit()
def set_tooltips_for_labels(self):
def process_child(child):
for g in child.children():
if isinstance(g, QLabel):
buddy = g.buddy()
if buddy is not None and hasattr(buddy, 'toolTip'):
htext = unicode(buddy.toolTip()).strip()
etext = unicode(g.toolTip()).strip()
if htext and not etext:
g.setToolTip(htext)
g.setWhatsThis(htext)
else:
process_child(g)
process_child(self.showing_widget)
def show_plugin(self, plugin): def show_plugin(self, plugin):
self.showing_widget = plugin.create_widget(self.scroll_area) self.showing_widget = plugin.create_widget(self.scroll_area)
self.showing_widget.genesis(self.gui) self.showing_widget.genesis(self.gui)
self.showing_widget.initialize() self.showing_widget.initialize()
self.set_tooltips_for_labels()
self.scroll_area.setWidget(self.showing_widget) self.scroll_area.setWidget(self.showing_widget)
self.stack.setCurrentIndex(1) self.stack.setCurrentIndex(1)
self.showing_widget.show() self.showing_widget.show()