mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
KG wip 0.7.33+
This commit is contained in:
commit
167fbe94a4
@ -5,6 +5,7 @@ newscientist.com
|
|||||||
'''
|
'''
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
import urllib
|
||||||
from calibre.web.feeds.news import BasicNewsRecipe
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
class NewScientist(BasicNewsRecipe):
|
class NewScientist(BasicNewsRecipe):
|
||||||
@ -24,7 +25,7 @@ class NewScientist(BasicNewsRecipe):
|
|||||||
needs_subscription = 'optional'
|
needs_subscription = 'optional'
|
||||||
extra_css = """
|
extra_css = """
|
||||||
body{font-family: Arial,sans-serif}
|
body{font-family: Arial,sans-serif}
|
||||||
img{margin-bottom: 0.8em}
|
img{margin-bottom: 0.8em; display: block}
|
||||||
.quotebx{font-size: x-large; font-weight: bold; margin-right: 2em; margin-left: 2em}
|
.quotebx{font-size: x-large; font-weight: bold; margin-right: 2em; margin-left: 2em}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -41,12 +42,14 @@ class NewScientist(BasicNewsRecipe):
|
|||||||
def get_browser(self):
|
def get_browser(self):
|
||||||
br = BasicNewsRecipe.get_browser()
|
br = BasicNewsRecipe.get_browser()
|
||||||
br.open('http://www.newscientist.com/')
|
br.open('http://www.newscientist.com/')
|
||||||
if self.username is not None and self.password is not None:
|
if self.username is not None and self.password is not None:
|
||||||
br.open('https://www.newscientist.com/user/login?redirectURL=')
|
br.open('https://www.newscientist.com/user/login')
|
||||||
br.select_form(nr=2)
|
data = urllib.urlencode({ 'source':'form'
|
||||||
br['loginId' ] = self.username
|
,'redirectURL':''
|
||||||
br['password'] = self.password
|
,'loginId':self.username
|
||||||
br.submit()
|
,'password':self.password
|
||||||
|
})
|
||||||
|
br.open('https://www.newscientist.com/user/login',data)
|
||||||
return br
|
return br
|
||||||
|
|
||||||
remove_tags = [
|
remove_tags = [
|
||||||
@ -55,21 +58,22 @@ class NewScientist(BasicNewsRecipe):
|
|||||||
,dict(name='p' , attrs={'class':['marker','infotext' ]})
|
,dict(name='p' , attrs={'class':['marker','infotext' ]})
|
||||||
,dict(name='meta' , attrs={'name' :'description' })
|
,dict(name='meta' , attrs={'name' :'description' })
|
||||||
,dict(name='a' , attrs={'rel' :'tag' })
|
,dict(name='a' , attrs={'rel' :'tag' })
|
||||||
|
,dict(name='ul' , attrs={'class':'markerlist' })
|
||||||
,dict(name=['link','base','meta','iframe','object','embed'])
|
,dict(name=['link','base','meta','iframe','object','embed'])
|
||||||
]
|
]
|
||||||
remove_tags_after = dict(attrs={'class':['nbpcopy','comments']})
|
remove_tags_after = dict(attrs={'class':['nbpcopy','comments']})
|
||||||
remove_attributes = ['height','width','lang']
|
remove_attributes = ['height','width','lang','onclick']
|
||||||
|
|
||||||
feeds = [
|
feeds = [
|
||||||
(u'Latest Headlines' , u'http://feeds.newscientist.com/science-news' )
|
(u'Latest Headlines' , u'http://feeds.newscientist.com/science-news' )
|
||||||
,(u'Magazine' , u'http://www.newscientist.com/feed/magazine' )
|
,(u'Magazine' , u'http://feeds.newscientist.com/magazine' )
|
||||||
,(u'Health' , u'http://www.newscientist.com/feed/view?id=2&type=channel' )
|
,(u'Health' , u'http://feeds.newscientist.com/health' )
|
||||||
,(u'Life' , u'http://www.newscientist.com/feed/view?id=3&type=channel' )
|
,(u'Life' , u'http://feeds.newscientist.com/life' )
|
||||||
,(u'Space' , u'http://www.newscientist.com/feed/view?id=6&type=channel' )
|
,(u'Space' , u'http://feeds.newscientist.com/space' )
|
||||||
,(u'Physics and Mathematics' , u'http://www.newscientist.com/feed/view?id=4&type=channel' )
|
,(u'Physics and Mathematics' , u'http://feeds.newscientist.com/physics-math' )
|
||||||
,(u'Environment' , u'http://www.newscientist.com/feed/view?id=1&type=channel' )
|
,(u'Environment' , u'http://feeds.newscientist.com/environment' )
|
||||||
,(u'Science in Society' , u'http://www.newscientist.com/feed/view?id=5&type=channel' )
|
,(u'Science in Society' , u'http://feeds.newscientist.com/science-in-society' )
|
||||||
,(u'Tech' , u'http://www.newscientist.com/feed/view?id=7&type=channel' )
|
,(u'Tech' , u'http://feeds.newscientist.com/tech' )
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_article_url(self, article):
|
def get_article_url(self, article):
|
||||||
@ -79,11 +83,21 @@ class NewScientist(BasicNewsRecipe):
|
|||||||
return url + '?full=true&print=true'
|
return url + '?full=true&print=true'
|
||||||
|
|
||||||
def preprocess_html(self, soup):
|
def preprocess_html(self, soup):
|
||||||
|
if soup.html.has_key('id'):
|
||||||
|
del soup.html['id']
|
||||||
|
for item in soup.findAll(style=True):
|
||||||
|
del item['style']
|
||||||
for item in soup.findAll(['quote','quotetext']):
|
for item in soup.findAll(['quote','quotetext']):
|
||||||
item.name='p'
|
item.name='p'
|
||||||
|
for item in soup.findAll(['xref','figref']):
|
||||||
|
tstr = item.string
|
||||||
|
item.replaceWith(tstr)
|
||||||
for tg in soup.findAll('a'):
|
for tg in soup.findAll('a'):
|
||||||
if tg.string == 'Home':
|
if tg.string == 'Home':
|
||||||
tg.parent.extract()
|
tg.parent.extract()
|
||||||
return self.adeify_images(soup)
|
else:
|
||||||
return self.adeify_images(soup)
|
if tg.string is not None:
|
||||||
|
tstr = tg.string
|
||||||
|
tg.replaceWith(tstr)
|
||||||
|
return soup
|
||||||
|
|
||||||
|
@ -13,14 +13,16 @@ class Radikal_tr(BasicNewsRecipe):
|
|||||||
description = 'News from Turkey'
|
description = 'News from Turkey'
|
||||||
publisher = 'radikal'
|
publisher = 'radikal'
|
||||||
category = 'news, politics, Turkey'
|
category = 'news, politics, Turkey'
|
||||||
oldest_article = 2
|
oldest_article = 7
|
||||||
max_articles_per_feed = 150
|
max_articles_per_feed = 150
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
encoding = 'cp1254'
|
encoding = 'cp1254'
|
||||||
use_embedded_content = False
|
use_embedded_content = False
|
||||||
masthead_url = 'http://www.radikal.com.tr/D/i/1/V2/radikal_logo.jpg'
|
masthead_url = 'http://www.radikal.com.tr/D/i/1/V2/radikal_logo.jpg'
|
||||||
language = 'tr'
|
language = 'tr'
|
||||||
extra_css = ' @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)} .article_description,body{font-family: Arial,Verdana,Helvetica,sans1,sans-serif } '
|
extra_css = """ @font-face {font-family: "sans1";src:url(res:///opt/sony/ebook/FONT/tt0003m_.ttf)}
|
||||||
|
.article_description,body{font-family: Arial,Verdana,Helvetica,sans1,sans-serif}
|
||||||
|
"""
|
||||||
|
|
||||||
conversion_options = {
|
conversion_options = {
|
||||||
'comment' : description
|
'comment' : description
|
||||||
@ -34,7 +36,13 @@ class Radikal_tr(BasicNewsRecipe):
|
|||||||
remove_tags_after = dict(attrs={'id':'haberDetayYazi'})
|
remove_tags_after = dict(attrs={'id':'haberDetayYazi'})
|
||||||
|
|
||||||
|
|
||||||
feeds = [(u'Yazarlar', u'http://www.radikal.com.tr/d/rss/RssYazarlar.xml')]
|
feeds = [
|
||||||
|
(u'Yazarlar' , u'http://www.radikal.com.tr/d/rss/RssYazarlar.xml')
|
||||||
|
,(u'Turkiye' , u'http://www.radikal.com.tr/d/rss/Rss_97.xml' )
|
||||||
|
,(u'Politika' , u'http://www.radikal.com.tr/d/rss/Rss_98.xml' )
|
||||||
|
,(u'Dis Haberler', u'http://www.radikal.com.tr/d/rss/Rss_100.xml' )
|
||||||
|
,(u'Ekonomi' , u'http://www.radikal.com.tr/d/rss/Rss_101.xml' )
|
||||||
|
]
|
||||||
|
|
||||||
def print_version(self, url):
|
def print_version(self, url):
|
||||||
articleid = url.rpartition('ArticleID=')[2]
|
articleid = url.rpartition('ArticleID=')[2]
|
||||||
|
@ -457,7 +457,8 @@ from calibre.devices.blackberry.driver import BLACKBERRY
|
|||||||
from calibre.devices.cybook.driver import CYBOOK, ORIZON
|
from calibre.devices.cybook.driver import CYBOOK, ORIZON
|
||||||
from calibre.devices.eb600.driver import EB600, COOL_ER, SHINEBOOK, \
|
from calibre.devices.eb600.driver import EB600, COOL_ER, SHINEBOOK, \
|
||||||
POCKETBOOK360, GER2, ITALICA, ECLICTO, DBOOK, INVESBOOK, \
|
POCKETBOOK360, GER2, ITALICA, ECLICTO, DBOOK, INVESBOOK, \
|
||||||
BOOQ, ELONEX, POCKETBOOK301, MENTOR, POCKETBOOK602
|
BOOQ, ELONEX, POCKETBOOK301, MENTOR, POCKETBOOK602, \
|
||||||
|
POCKETBOOK701
|
||||||
from calibre.devices.iliad.driver import ILIAD
|
from calibre.devices.iliad.driver import ILIAD
|
||||||
from calibre.devices.irexdr.driver import IREXDR1000, IREXDR800
|
from calibre.devices.irexdr.driver import IREXDR1000, IREXDR800
|
||||||
from calibre.devices.jetbook.driver import JETBOOK, MIBUK, JETBOOK_MINI
|
from calibre.devices.jetbook.driver import JETBOOK, MIBUK, JETBOOK_MINI
|
||||||
@ -545,9 +546,7 @@ plugins += [
|
|||||||
JETBOOK_MINI,
|
JETBOOK_MINI,
|
||||||
MIBUK,
|
MIBUK,
|
||||||
SHINEBOOK,
|
SHINEBOOK,
|
||||||
POCKETBOOK360,
|
POCKETBOOK360, POCKETBOOK301, POCKETBOOK602, POCKETBOOK701,
|
||||||
POCKETBOOK301,
|
|
||||||
POCKETBOOK602,
|
|
||||||
KINDLE,
|
KINDLE,
|
||||||
KINDLE2,
|
KINDLE2,
|
||||||
KINDLE_DX,
|
KINDLE_DX,
|
||||||
|
@ -246,3 +246,23 @@ class POCKETBOOK602(USBMS):
|
|||||||
VENDOR_NAME = ''
|
VENDOR_NAME = ''
|
||||||
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = ['PB602', 'PB902']
|
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = ['PB602', 'PB902']
|
||||||
|
|
||||||
|
class POCKETBOOK701(USBMS):
|
||||||
|
|
||||||
|
name = 'PocketBook 701 Device Interface'
|
||||||
|
description = _('Communicate with the PocketBook 701')
|
||||||
|
author = _('Kovid Goyal')
|
||||||
|
|
||||||
|
supported_platforms = ['windows', 'osx', 'linux']
|
||||||
|
FORMATS = ['epub', 'fb2', 'prc', 'mobi', 'pdf', 'djvu', 'rtf', 'chm',
|
||||||
|
'doc', 'tcr', 'txt']
|
||||||
|
|
||||||
|
EBOOK_DIR_MAIN = 'books'
|
||||||
|
SUPPORTS_SUB_DIRS = True
|
||||||
|
|
||||||
|
VENDOR_ID = [0x18d1]
|
||||||
|
PRODUCT_ID = [0xa004]
|
||||||
|
BCD = [0x0224]
|
||||||
|
|
||||||
|
VENDOR_NAME = 'ANDROID'
|
||||||
|
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = '__UMS_COMPOSITE'
|
||||||
|
|
||||||
|
@ -23,16 +23,16 @@ class SNE(USBMS):
|
|||||||
FORMATS = ['epub', 'pdf', 'txt']
|
FORMATS = ['epub', 'pdf', 'txt']
|
||||||
|
|
||||||
VENDOR_ID = [0x04e8]
|
VENDOR_ID = [0x04e8]
|
||||||
PRODUCT_ID = [0x2051, 0x2053]
|
PRODUCT_ID = [0x2051, 0x2053, 0x2054]
|
||||||
BCD = [0x0323]
|
BCD = [0x0323]
|
||||||
|
|
||||||
VENDOR_NAME = 'SAMSUNG'
|
VENDOR_NAME = 'SAMSUNG'
|
||||||
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = 'SNE-60'
|
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = ['SNE-60', 'E65']
|
||||||
|
|
||||||
MAIN_MEMORY_VOLUME_LABEL = 'SNE Main Memory'
|
MAIN_MEMORY_VOLUME_LABEL = 'SNE Main Memory'
|
||||||
STORAGE_CARD_VOLUME_LABEL = 'SNE Storage Card'
|
STORAGE_CARD_VOLUME_LABEL = 'SNE Storage Card'
|
||||||
|
|
||||||
EBOOK_DIR_MAIN = 'Books'
|
EBOOK_DIR_MAIN = EBOOK_DIR_CARD_A = 'Books'
|
||||||
SUPPORTS_SUB_DIRS = True
|
SUPPORTS_SUB_DIRS = True
|
||||||
|
|
||||||
|
|
||||||
|
@ -22,6 +22,9 @@ class UnknownFormatError(Exception):
|
|||||||
class DRMError(ValueError):
|
class DRMError(ValueError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
class ParserError(ValueError):
|
||||||
|
pass
|
||||||
|
|
||||||
BOOK_EXTENSIONS = ['lrf', 'rar', 'zip', 'rtf', 'lit', 'txt', 'htm', 'xhtm',
|
BOOK_EXTENSIONS = ['lrf', 'rar', 'zip', 'rtf', 'lit', 'txt', 'htm', 'xhtm',
|
||||||
'html', 'xhtml', 'pdf', 'pdb', 'pdr', 'prc', 'mobi', 'azw', 'doc',
|
'html', 'xhtml', 'pdf', 'pdb', 'pdr', 'prc', 'mobi', 'azw', 'doc',
|
||||||
'epub', 'fb2', 'djvu', 'lrx', 'cbr', 'cbz', 'cbc', 'oebzip',
|
'epub', 'fb2', 'djvu', 'lrx', 'cbr', 'cbz', 'cbc', 'oebzip',
|
||||||
@ -39,6 +42,10 @@ class HTMLRenderer(object):
|
|||||||
try:
|
try:
|
||||||
if not ok:
|
if not ok:
|
||||||
raise RuntimeError('Rendering of HTML failed.')
|
raise RuntimeError('Rendering of HTML failed.')
|
||||||
|
de = self.page.mainFrame().documentElement()
|
||||||
|
pe = de.findFirst('parsererror')
|
||||||
|
if not pe.isNull():
|
||||||
|
raise ParserError(pe.toPlainText())
|
||||||
image = QImage(self.page.viewportSize(), QImage.Format_ARGB32)
|
image = QImage(self.page.viewportSize(), QImage.Format_ARGB32)
|
||||||
image.setDotsPerMeterX(96*(100/2.54))
|
image.setDotsPerMeterX(96*(100/2.54))
|
||||||
image.setDotsPerMeterY(96*(100/2.54))
|
image.setDotsPerMeterY(96*(100/2.54))
|
||||||
@ -104,7 +111,7 @@ def render_html_svg_workaround(path_to_html, log, width=590, height=750):
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
def render_html(path_to_html, width=590, height=750):
|
def render_html(path_to_html, width=590, height=750, as_xhtml=True):
|
||||||
from PyQt4.QtWebKit import QWebPage
|
from PyQt4.QtWebKit import QWebPage
|
||||||
from PyQt4.Qt import QEventLoop, QPalette, Qt, SIGNAL, QUrl, QSize
|
from PyQt4.Qt import QEventLoop, QPalette, Qt, SIGNAL, QUrl, QSize
|
||||||
from calibre.gui2 import is_ok_to_use_qt
|
from calibre.gui2 import is_ok_to_use_qt
|
||||||
@ -122,11 +129,18 @@ def render_html(path_to_html, width=590, height=750):
|
|||||||
renderer = HTMLRenderer(page, loop)
|
renderer = HTMLRenderer(page, loop)
|
||||||
page.connect(page, SIGNAL('loadFinished(bool)'), renderer,
|
page.connect(page, SIGNAL('loadFinished(bool)'), renderer,
|
||||||
Qt.QueuedConnection)
|
Qt.QueuedConnection)
|
||||||
page.mainFrame().load(QUrl.fromLocalFile(path_to_html))
|
if as_xhtml:
|
||||||
|
page.mainFrame().setContent(open(path_to_html, 'rb').read(),
|
||||||
|
'application/xhtml+xml', QUrl.fromLocalFile(path_to_html))
|
||||||
|
else:
|
||||||
|
page.mainFrame().load(QUrl.fromLocalFile(path_to_html))
|
||||||
loop.exec_()
|
loop.exec_()
|
||||||
renderer.loop = renderer.page = None
|
renderer.loop = renderer.page = None
|
||||||
del page
|
del page
|
||||||
del loop
|
del loop
|
||||||
|
if isinstance(renderer.exception, ParserError) and as_xhtml:
|
||||||
|
return render_html(path_to_html, width=width, height=height,
|
||||||
|
as_xhtml=False)
|
||||||
return renderer
|
return renderer
|
||||||
|
|
||||||
def check_ebook_format(stream, current_guess):
|
def check_ebook_format(stream, current_guess):
|
||||||
|
@ -17,6 +17,7 @@ pdfreflow, pdfreflow_error = plugins['pdfreflow']
|
|||||||
def get_metadata(stream, cover=True):
|
def get_metadata(stream, cover=True):
|
||||||
if pdfreflow is None:
|
if pdfreflow is None:
|
||||||
raise RuntimeError(pdfreflow_error)
|
raise RuntimeError(pdfreflow_error)
|
||||||
|
stream.seek(0)
|
||||||
raw = stream.read()
|
raw = stream.read()
|
||||||
#isbn = _isbn_pat.search(raw)
|
#isbn = _isbn_pat.search(raw)
|
||||||
#if isbn is not None:
|
#if isbn is not None:
|
||||||
|
@ -205,7 +205,10 @@ class Stylizer(object):
|
|||||||
NameError, # thrown on OS X instead of SelectorSyntaxError
|
NameError, # thrown on OS X instead of SelectorSyntaxError
|
||||||
SelectorSyntaxError):
|
SelectorSyntaxError):
|
||||||
continue
|
continue
|
||||||
matches = selector(tree)
|
try:
|
||||||
|
matches = selector(tree)
|
||||||
|
except etree.XPathEvalError:
|
||||||
|
continue
|
||||||
|
|
||||||
if not matches:
|
if not matches:
|
||||||
ntext = capital_sel_pat.sub(lambda m: m.group().lower(), text)
|
ntext = capital_sel_pat.sub(lambda m: m.group().lower(), text)
|
||||||
|
@ -593,7 +593,6 @@ class DeviceMenu(QMenu): # {{{
|
|||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
class DeviceMixin(object): # {{{
|
class DeviceMixin(object): # {{{
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -7,14 +7,14 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>479</width>
|
<width>479</width>
|
||||||
<height>606</height>
|
<height>591</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>Configure Ebook viewer</string>
|
<string>Configure Ebook viewer</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowIcon">
|
<property name="windowIcon">
|
||||||
<iconset>
|
<iconset resource="../../../../resources/images.qrc">
|
||||||
<normaloff>:/images/config.png</normaloff>:/images/config.png</iconset>
|
<normaloff>:/images/config.png</normaloff>:/images/config.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout_4">
|
<layout class="QGridLayout" name="gridLayout_4">
|
||||||
@ -85,11 +85,7 @@
|
|||||||
<item row="2" column="1">
|
<item row="2" column="1">
|
||||||
<widget class="QFontComboBox" name="mono_family"/>
|
<widget class="QFontComboBox" name="mono_family"/>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
<item row="3" column="0">
|
||||||
</item>
|
|
||||||
<item row="1" column="0">
|
|
||||||
<layout class="QGridLayout" name="gridLayout_2">
|
|
||||||
<item row="0" column="0">
|
|
||||||
<widget class="QLabel" name="label_4">
|
<widget class="QLabel" name="label_4">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>&Default font size:</string>
|
<string>&Default font size:</string>
|
||||||
@ -99,7 +95,7 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="1">
|
<item row="3" column="1">
|
||||||
<widget class="QSpinBox" name="default_font_size">
|
<widget class="QSpinBox" name="default_font_size">
|
||||||
<property name="suffix">
|
<property name="suffix">
|
||||||
<string> px</string>
|
<string> px</string>
|
||||||
@ -112,7 +108,7 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0">
|
<item row="4" column="0">
|
||||||
<widget class="QLabel" name="label_5">
|
<widget class="QLabel" name="label_5">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Monospace &font size:</string>
|
<string>Monospace &font size:</string>
|
||||||
@ -122,7 +118,7 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="1">
|
<item row="4" column="1">
|
||||||
<widget class="QSpinBox" name="mono_font_size">
|
<widget class="QSpinBox" name="mono_font_size">
|
||||||
<property name="suffix">
|
<property name="suffix">
|
||||||
<string> px</string>
|
<string> px</string>
|
||||||
@ -135,7 +131,7 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="0">
|
<item row="5" column="0">
|
||||||
<widget class="QLabel" name="label_6">
|
<widget class="QLabel" name="label_6">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>S&tandard font:</string>
|
<string>S&tandard font:</string>
|
||||||
@ -145,7 +141,7 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="1">
|
<item row="5" column="1">
|
||||||
<widget class="QComboBox" name="standard_font">
|
<widget class="QComboBox" name="standard_font">
|
||||||
<item>
|
<item>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@ -164,91 +160,125 @@
|
|||||||
</item>
|
</item>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="7" column="0" colspan="2">
|
|
||||||
<widget class="QCheckBox" name="opt_remember_window_size">
|
|
||||||
<property name="text">
|
|
||||||
<string>Remember last used &window size</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="8" column="0" colspan="2">
|
|
||||||
<widget class="QCheckBox" name="opt_remember_current_page">
|
|
||||||
<property name="text">
|
|
||||||
<string>Remember the &current page when quitting</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="1">
|
|
||||||
<widget class="QSpinBox" name="max_view_width">
|
|
||||||
<property name="suffix">
|
|
||||||
<string> px</string>
|
|
||||||
</property>
|
|
||||||
<property name="minimum">
|
|
||||||
<number>100</number>
|
|
||||||
</property>
|
|
||||||
<property name="maximum">
|
|
||||||
<number>10000</number>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="0">
|
|
||||||
<widget class="QLabel" name="label_7">
|
|
||||||
<property name="text">
|
|
||||||
<string>Maximum &view width:</string>
|
|
||||||
</property>
|
|
||||||
<property name="buddy">
|
|
||||||
<cstring>max_view_width</cstring>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="4" column="0" colspan="2">
|
|
||||||
<widget class="QCheckBox" name="hyphenate">
|
|
||||||
<property name="text">
|
|
||||||
<string>H&yphenate (break line in the middle of large words)</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="5" column="1">
|
|
||||||
<widget class="QComboBox" name="hyphenate_default_lang">
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>The default language to use for hyphenation rules. If the book does not specify a language, this will be used.</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="5" column="0">
|
|
||||||
<widget class="QLabel" name="label_8">
|
|
||||||
<property name="text">
|
|
||||||
<string>Default &language for hyphenation:</string>
|
|
||||||
</property>
|
|
||||||
<property name="buddy">
|
|
||||||
<cstring>hyphenate_default_lang</cstring>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="6" column="0" colspan="2">
|
|
||||||
<widget class="QCheckBox" name="opt_fit_images">
|
|
||||||
<property name="text">
|
|
||||||
<string>&Resize images larger than the viewer window (needs restart)</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0">
|
|
||||||
<widget class="QGroupBox" name="groupBox_2">
|
|
||||||
<property name="title">
|
|
||||||
<string>&User stylesheet</string>
|
|
||||||
</property>
|
|
||||||
<layout class="QGridLayout" name="gridLayout_5">
|
|
||||||
<item row="1" column="0">
|
|
||||||
<widget class="QPlainTextEdit" name="css"/>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QGridLayout" name="gridLayout_2">
|
||||||
|
<item row="8" column="0" colspan="2">
|
||||||
|
<widget class="QCheckBox" name="opt_remember_window_size">
|
||||||
|
<property name="text">
|
||||||
|
<string>Remember last used &window size</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="9" column="0" colspan="2">
|
||||||
|
<widget class="QCheckBox" name="opt_remember_current_page">
|
||||||
|
<property name="text">
|
||||||
|
<string>Remember the &current page when quitting</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="0" colspan="2">
|
||||||
|
<widget class="QCheckBox" name="hyphenate">
|
||||||
|
<property name="text">
|
||||||
|
<string>H&yphenate (break line in the middle of large words)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="1">
|
||||||
|
<widget class="QComboBox" name="hyphenate_default_lang">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>The default language to use for hyphenation rules. If the book does not specify a language, this will be used.</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="0">
|
||||||
|
<widget class="QLabel" name="label_8">
|
||||||
|
<property name="text">
|
||||||
|
<string>Default &language for hyphenation:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>hyphenate_default_lang</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="7" column="0" colspan="2">
|
||||||
|
<widget class="QCheckBox" name="opt_fit_images">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Resize images larger than the viewer window (needs restart)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0">
|
||||||
|
<widget class="QLabel" name="label_11">
|
||||||
|
<property name="text">
|
||||||
|
<string>Page flip &duration:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>opt_page_flip_duration</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="1">
|
||||||
|
<widget class="QDoubleSpinBox" name="opt_page_flip_duration">
|
||||||
|
<property name="specialValueText">
|
||||||
|
<string>disabled</string>
|
||||||
|
</property>
|
||||||
|
<property name="suffix">
|
||||||
|
<string> secs</string>
|
||||||
|
</property>
|
||||||
|
<property name="decimals">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<double>0.100000000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<double>3.000000000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="singleStep">
|
||||||
|
<double>0.100000000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<double>0.500000000000000</double>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="10" column="0" colspan="2">
|
||||||
|
<widget class="QCheckBox" name="opt_wheel_flips_pages">
|
||||||
|
<property name="text">
|
||||||
|
<string>Mouse &wheel flips pages</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QSpinBox" name="max_view_width">
|
||||||
|
<property name="suffix">
|
||||||
|
<string> px</string>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<number>100</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>10000</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="label_7">
|
||||||
|
<property name="text">
|
||||||
|
<string>Maximum &view width:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>max_view_width</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="tab_2">
|
<widget class="QWidget" name="tab_2">
|
||||||
@ -268,6 +298,29 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
<widget class="QWidget" name="tab_3">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>User &Stylesheet</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_10">
|
||||||
|
<property name="text">
|
||||||
|
<string><p>A CSS stylesheet that can be used to control the look and feel of books. For examples, click <a href="http://www.mobileread.com/forums/showthread.php?t=51500">here</a>.</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="openExternalLinks">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPlainTextEdit" name="css"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
@ -276,12 +329,8 @@
|
|||||||
<tabstop>serif_family</tabstop>
|
<tabstop>serif_family</tabstop>
|
||||||
<tabstop>sans_family</tabstop>
|
<tabstop>sans_family</tabstop>
|
||||||
<tabstop>mono_family</tabstop>
|
<tabstop>mono_family</tabstop>
|
||||||
<tabstop>default_font_size</tabstop>
|
|
||||||
<tabstop>mono_font_size</tabstop>
|
|
||||||
<tabstop>standard_font</tabstop>
|
|
||||||
<tabstop>max_view_width</tabstop>
|
<tabstop>max_view_width</tabstop>
|
||||||
<tabstop>opt_remember_window_size</tabstop>
|
<tabstop>opt_remember_window_size</tabstop>
|
||||||
<tabstop>css</tabstop>
|
|
||||||
<tabstop>buttonBox</tabstop>
|
<tabstop>buttonBox</tabstop>
|
||||||
</tabstops>
|
</tabstops>
|
||||||
<resources>
|
<resources>
|
||||||
|
@ -18,6 +18,7 @@ from PyQt4.QtWebKit import QWebPage, QWebView, QWebSettings
|
|||||||
from calibre.utils.config import Config, StringConfig
|
from calibre.utils.config import Config, StringConfig
|
||||||
from calibre.utils.localization import get_language
|
from calibre.utils.localization import get_language
|
||||||
from calibre.gui2.viewer.config_ui import Ui_Dialog
|
from calibre.gui2.viewer.config_ui import Ui_Dialog
|
||||||
|
from calibre.gui2.viewer.flip import SlideFlip
|
||||||
from calibre.gui2.shortcuts import Shortcuts, ShortcutConfig
|
from calibre.gui2.shortcuts import Shortcuts, ShortcutConfig
|
||||||
from calibre.constants import iswindows
|
from calibre.constants import iswindows
|
||||||
from calibre import prints, guess_type
|
from calibre import prints, guess_type
|
||||||
@ -52,6 +53,11 @@ def config(defaults=None):
|
|||||||
help=_('Default language for hyphenation rules'))
|
help=_('Default language for hyphenation rules'))
|
||||||
c.add_opt('remember_current_page', default=True,
|
c.add_opt('remember_current_page', default=True,
|
||||||
help=_('Save the current position in the document, when quitting'))
|
help=_('Save the current position in the document, when quitting'))
|
||||||
|
c.add_opt('wheel_flips_pages', default=False,
|
||||||
|
help=_('Have the mouse wheel turn pages'))
|
||||||
|
c.add_opt('page_flip_duration', default=0.5,
|
||||||
|
help=_('The time, in seconds, for the page flip animation. Default'
|
||||||
|
' is half a second.'))
|
||||||
|
|
||||||
fonts = c.add_group('FONTS', _('Font options'))
|
fonts = c.add_group('FONTS', _('Font options'))
|
||||||
fonts('serif_family', default='Times New Roman' if iswindows else 'Liberation Serif',
|
fonts('serif_family', default='Times New Roman' if iswindows else 'Liberation Serif',
|
||||||
@ -75,6 +81,8 @@ class ConfigDialog(QDialog, Ui_Dialog):
|
|||||||
opts = config().parse()
|
opts = config().parse()
|
||||||
self.opt_remember_window_size.setChecked(opts.remember_window_size)
|
self.opt_remember_window_size.setChecked(opts.remember_window_size)
|
||||||
self.opt_remember_current_page.setChecked(opts.remember_current_page)
|
self.opt_remember_current_page.setChecked(opts.remember_current_page)
|
||||||
|
self.opt_wheel_flips_pages.setChecked(opts.wheel_flips_pages)
|
||||||
|
self.opt_page_flip_duration.setValue(opts.page_flip_duration)
|
||||||
self.serif_family.setCurrentFont(QFont(opts.serif_family))
|
self.serif_family.setCurrentFont(QFont(opts.serif_family))
|
||||||
self.sans_family.setCurrentFont(QFont(opts.sans_family))
|
self.sans_family.setCurrentFont(QFont(opts.sans_family))
|
||||||
self.mono_family.setCurrentFont(QFont(opts.mono_family))
|
self.mono_family.setCurrentFont(QFont(opts.mono_family))
|
||||||
@ -122,6 +130,8 @@ class ConfigDialog(QDialog, Ui_Dialog):
|
|||||||
c.set('max_view_width', int(self.max_view_width.value()))
|
c.set('max_view_width', int(self.max_view_width.value()))
|
||||||
c.set('hyphenate', self.hyphenate.isChecked())
|
c.set('hyphenate', self.hyphenate.isChecked())
|
||||||
c.set('remember_current_page', self.opt_remember_current_page.isChecked())
|
c.set('remember_current_page', self.opt_remember_current_page.isChecked())
|
||||||
|
c.set('wheel_flips_pages', self.opt_wheel_flips_pages.isChecked())
|
||||||
|
c.set('page_flip_duration', self.opt_page_flip_duration.value())
|
||||||
idx = self.hyphenate_default_lang.currentIndex()
|
idx = self.hyphenate_default_lang.currentIndex()
|
||||||
c.set('hyphenate_default_lang',
|
c.set('hyphenate_default_lang',
|
||||||
str(self.hyphenate_default_lang.itemData(idx).toString()))
|
str(self.hyphenate_default_lang.itemData(idx).toString()))
|
||||||
@ -197,6 +207,9 @@ class Document(QWebPage):
|
|||||||
self.hyphenate = opts.hyphenate
|
self.hyphenate = opts.hyphenate
|
||||||
self.hyphenate_default_lang = opts.hyphenate_default_lang
|
self.hyphenate_default_lang = opts.hyphenate_default_lang
|
||||||
self.do_fit_images = opts.fit_images
|
self.do_fit_images = opts.fit_images
|
||||||
|
self.page_flip_duration = opts.page_flip_duration
|
||||||
|
self.enable_page_flip = self.page_flip_duration > 0.1
|
||||||
|
self.wheel_flips_pages = opts.wheel_flips_pages
|
||||||
|
|
||||||
def fit_images(self):
|
def fit_images(self):
|
||||||
if self.do_fit_images:
|
if self.do_fit_images:
|
||||||
@ -453,6 +466,8 @@ class DocumentView(QWebView):
|
|||||||
|
|
||||||
def __init__(self, *args):
|
def __init__(self, *args):
|
||||||
QWebView.__init__(self, *args)
|
QWebView.__init__(self, *args)
|
||||||
|
self.flipper = SlideFlip(self)
|
||||||
|
self.is_auto_repeat_event = False
|
||||||
self.debug_javascript = False
|
self.debug_javascript = False
|
||||||
self.shortcuts = Shortcuts(SHORTCUTS, 'shortcuts/viewer')
|
self.shortcuts = Shortcuts(SHORTCUTS, 'shortcuts/viewer')
|
||||||
self.self_closing_pat = re.compile(r'<([a-z1-6]+)\s+([^>]+)/>',
|
self.self_closing_pat = re.compile(r'<([a-z1-6]+)\s+([^>]+)/>',
|
||||||
@ -693,6 +708,13 @@ class DocumentView(QWebView):
|
|||||||
self.manager.scrolled(self.document.scroll_fraction)
|
self.manager.scrolled(self.document.scroll_fraction)
|
||||||
|
|
||||||
self.turn_off_internal_scrollbars()
|
self.turn_off_internal_scrollbars()
|
||||||
|
if self.flipper.isVisible():
|
||||||
|
if self.flipper.running:
|
||||||
|
self.flipper.setVisible(False)
|
||||||
|
else:
|
||||||
|
self.flipper(self.current_page_image(),
|
||||||
|
duration=self.document.page_flip_duration)
|
||||||
|
|
||||||
|
|
||||||
def turn_off_internal_scrollbars(self):
|
def turn_off_internal_scrollbars(self):
|
||||||
self.document.mainFrame().setScrollBarPolicy(Qt.Vertical, Qt.ScrollBarAlwaysOff)
|
self.document.mainFrame().setScrollBarPolicy(Qt.Vertical, Qt.ScrollBarAlwaysOff)
|
||||||
@ -708,12 +730,17 @@ class DocumentView(QWebView):
|
|||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def find_next_blank_line(self, overlap):
|
def current_page_image(self, overlap=-1):
|
||||||
|
if overlap < 0:
|
||||||
|
overlap = self.height()
|
||||||
img = QImage(self.width(), overlap, QImage.Format_ARGB32)
|
img = QImage(self.width(), overlap, QImage.Format_ARGB32)
|
||||||
painter = QPainter(img)
|
painter = QPainter(img)
|
||||||
# Render a region of width x overlap pixels atthe bottom of the current viewport
|
|
||||||
self.document.mainFrame().render(painter, QRegion(0, 0, self.width(), overlap))
|
self.document.mainFrame().render(painter, QRegion(0, 0, self.width(), overlap))
|
||||||
painter.end()
|
painter.end()
|
||||||
|
return img
|
||||||
|
|
||||||
|
def find_next_blank_line(self, overlap):
|
||||||
|
img = self.current_page_image(overlap)
|
||||||
for i in range(overlap-1, -1, -1):
|
for i in range(overlap-1, -1, -1):
|
||||||
if self.test_line(img, i):
|
if self.test_line(img, i):
|
||||||
self.scroll_by(y=i, notify=False)
|
self.scroll_by(y=i, notify=False)
|
||||||
@ -721,22 +748,42 @@ class DocumentView(QWebView):
|
|||||||
self.scroll_by(y=overlap)
|
self.scroll_by(y=overlap)
|
||||||
|
|
||||||
def previous_page(self):
|
def previous_page(self):
|
||||||
|
if self.flipper.running and not self.is_auto_repeat_event:
|
||||||
|
return
|
||||||
|
if self.loading_url is not None:
|
||||||
|
return
|
||||||
|
epf = self.document.enable_page_flip and not self.is_auto_repeat_event
|
||||||
|
|
||||||
delta_y = self.document.window_height - 25
|
delta_y = self.document.window_height - 25
|
||||||
if self.document.at_top:
|
if self.document.at_top:
|
||||||
if self.manager is not None:
|
if self.manager is not None:
|
||||||
self.to_bottom = True
|
self.to_bottom = True
|
||||||
self.manager.previous_document()
|
if epf:
|
||||||
|
self.flipper.initialize(self.current_page_image(), False)
|
||||||
|
self.manager.previous_document()
|
||||||
else:
|
else:
|
||||||
opos = self.document.ypos
|
opos = self.document.ypos
|
||||||
upper_limit = opos - delta_y
|
upper_limit = opos - delta_y
|
||||||
if upper_limit < 0:
|
if upper_limit < 0:
|
||||||
upper_limit = 0
|
upper_limit = 0
|
||||||
if upper_limit < opos:
|
if upper_limit < opos:
|
||||||
|
if epf:
|
||||||
|
self.flipper.initialize(self.current_page_image(),
|
||||||
|
forwards=False)
|
||||||
self.document.scroll_to(self.document.xpos, upper_limit)
|
self.document.scroll_to(self.document.xpos, upper_limit)
|
||||||
|
if epf:
|
||||||
|
self.flipper(self.current_page_image(),
|
||||||
|
duration=self.document.page_flip_duration)
|
||||||
if self.manager is not None:
|
if self.manager is not None:
|
||||||
self.manager.scrolled(self.scroll_fraction)
|
self.manager.scrolled(self.scroll_fraction)
|
||||||
|
|
||||||
def next_page(self):
|
def next_page(self):
|
||||||
|
if self.flipper.running and not self.is_auto_repeat_event:
|
||||||
|
return
|
||||||
|
if self.loading_url is not None:
|
||||||
|
return
|
||||||
|
epf = self.document.enable_page_flip and not self.is_auto_repeat_event
|
||||||
|
|
||||||
window_height = self.document.window_height
|
window_height = self.document.window_height
|
||||||
document_height = self.document.height
|
document_height = self.document.height
|
||||||
ddelta = document_height - window_height
|
ddelta = document_height - window_height
|
||||||
@ -746,6 +793,8 @@ class DocumentView(QWebView):
|
|||||||
delta_y = window_height - 25
|
delta_y = window_height - 25
|
||||||
if self.document.at_bottom or ddelta <= 0:
|
if self.document.at_bottom or ddelta <= 0:
|
||||||
if self.manager is not None:
|
if self.manager is not None:
|
||||||
|
if epf:
|
||||||
|
self.flipper.initialize(self.current_page_image())
|
||||||
self.manager.next_document()
|
self.manager.next_document()
|
||||||
elif ddelta < 25:
|
elif ddelta < 25:
|
||||||
self.scroll_by(y=ddelta)
|
self.scroll_by(y=ddelta)
|
||||||
@ -758,6 +807,8 @@ class DocumentView(QWebView):
|
|||||||
#print 'After set padding=0:', self.document.ypos
|
#print 'After set padding=0:', self.document.ypos
|
||||||
if opos < oopos:
|
if opos < oopos:
|
||||||
if self.manager is not None:
|
if self.manager is not None:
|
||||||
|
if epf:
|
||||||
|
self.flipper.initialize(self.current_page_image())
|
||||||
self.manager.next_document()
|
self.manager.next_document()
|
||||||
return
|
return
|
||||||
lower_limit = opos + delta_y # Max value of top y co-ord after scrolling
|
lower_limit = opos + delta_y # Max value of top y co-ord after scrolling
|
||||||
@ -766,10 +817,14 @@ class DocumentView(QWebView):
|
|||||||
padding = lower_limit - max_y
|
padding = lower_limit - max_y
|
||||||
if padding == window_height:
|
if padding == window_height:
|
||||||
if self.manager is not None:
|
if self.manager is not None:
|
||||||
|
if epf:
|
||||||
|
self.flipper.initialize(self.current_page_image())
|
||||||
self.manager.next_document()
|
self.manager.next_document()
|
||||||
return
|
return
|
||||||
#print 'Setting padding to:', lower_limit - max_y
|
#print 'Setting padding to:', lower_limit - max_y
|
||||||
self.document.set_bottom_padding(lower_limit - max_y)
|
self.document.set_bottom_padding(lower_limit - max_y)
|
||||||
|
if epf:
|
||||||
|
self.flipper.initialize(self.current_page_image())
|
||||||
#print 'Document height:', self.document.height
|
#print 'Document height:', self.document.height
|
||||||
max_y = self.document.height - window_height
|
max_y = self.document.height - window_height
|
||||||
lower_limit = min(max_y, lower_limit)
|
lower_limit = min(max_y, lower_limit)
|
||||||
@ -780,6 +835,9 @@ class DocumentView(QWebView):
|
|||||||
#print 'After scroll pos:', self.document.ypos
|
#print 'After scroll pos:', self.document.ypos
|
||||||
self.find_next_blank_line(window_height - actually_scrolled)
|
self.find_next_blank_line(window_height - actually_scrolled)
|
||||||
#print 'After blank line pos:', self.document.ypos
|
#print 'After blank line pos:', self.document.ypos
|
||||||
|
if epf:
|
||||||
|
self.flipper(self.current_page_image(),
|
||||||
|
duration=self.document.page_flip_duration)
|
||||||
if self.manager is not None:
|
if self.manager is not None:
|
||||||
self.manager.scrolled(self.scroll_fraction)
|
self.manager.scrolled(self.scroll_fraction)
|
||||||
#print 'After all:', self.document.ypos
|
#print 'After all:', self.document.ypos
|
||||||
@ -833,6 +891,10 @@ class DocumentView(QWebView):
|
|||||||
|
|
||||||
def wheelEvent(self, event):
|
def wheelEvent(self, event):
|
||||||
if event.delta() < -14:
|
if event.delta() < -14:
|
||||||
|
if self.document.wheel_flips_pages:
|
||||||
|
self.next_page()
|
||||||
|
event.accept()
|
||||||
|
return
|
||||||
if self.document.at_bottom:
|
if self.document.at_bottom:
|
||||||
self.scroll_by(y=15) # at_bottom can lie on windows
|
self.scroll_by(y=15) # at_bottom can lie on windows
|
||||||
if self.manager is not None:
|
if self.manager is not None:
|
||||||
@ -840,6 +902,11 @@ class DocumentView(QWebView):
|
|||||||
event.accept()
|
event.accept()
|
||||||
return
|
return
|
||||||
elif event.delta() > 14:
|
elif event.delta() > 14:
|
||||||
|
if self.document.wheel_flips_pages:
|
||||||
|
self.previous_page()
|
||||||
|
event.accept()
|
||||||
|
return
|
||||||
|
|
||||||
if self.document.at_top:
|
if self.document.at_top:
|
||||||
if self.manager is not None:
|
if self.manager is not None:
|
||||||
self.manager.previous_document()
|
self.manager.previous_document()
|
||||||
@ -862,7 +929,11 @@ class DocumentView(QWebView):
|
|||||||
key = self.shortcuts.get_match(event)
|
key = self.shortcuts.get_match(event)
|
||||||
func = self.goto_location_actions.get(key, None)
|
func = self.goto_location_actions.get(key, None)
|
||||||
if func is not None:
|
if func is not None:
|
||||||
func()
|
self.is_auto_repeat_event = event.isAutoRepeat()
|
||||||
|
try:
|
||||||
|
func()
|
||||||
|
finally:
|
||||||
|
self.is_auto_repeat_event = False
|
||||||
elif key == 'Down':
|
elif key == 'Down':
|
||||||
self.scroll_by(y=15)
|
self.scroll_by(y=15)
|
||||||
elif key == 'Up':
|
elif key == 'Up':
|
||||||
|
116
src/calibre/gui2/viewer/flip.py
Normal file
116
src/calibre/gui2/viewer/flip.py
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
from PyQt4.Qt import QWidget, QPainter, QPropertyAnimation, QEasingCurve, \
|
||||||
|
QRect, QPixmap, Qt, pyqtProperty
|
||||||
|
|
||||||
|
class SlideFlip(QWidget):
|
||||||
|
|
||||||
|
# API {{{
|
||||||
|
|
||||||
|
# In addition the isVisible() and setVisible() methods must be present
|
||||||
|
|
||||||
|
def __init__(self, parent):
|
||||||
|
QWidget.__init__(self, parent)
|
||||||
|
|
||||||
|
self.setGeometry(0, 0, 1, 1)
|
||||||
|
self._current_width = 0
|
||||||
|
self.before_image = self.after_image = None
|
||||||
|
self.animation = QPropertyAnimation(self, 'current_width', self)
|
||||||
|
self.setVisible(False)
|
||||||
|
self.animation.valueChanged.connect(self.update)
|
||||||
|
self.animation.finished.connect(self.finished)
|
||||||
|
self.flip_forwards = True
|
||||||
|
self.setAttribute(Qt.WA_OpaquePaintEvent)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def running(self):
|
||||||
|
'True iff animation is currently running'
|
||||||
|
return self.animation.state() == self.animation.Running
|
||||||
|
|
||||||
|
def initialize(self, image, forwards=True):
|
||||||
|
'''
|
||||||
|
Initialize the flipper, causes the flipper to show itself displaying
|
||||||
|
the full `image`.
|
||||||
|
|
||||||
|
:param image: The image to display as background
|
||||||
|
:param forwards: If True flipper will flip forwards, otherwise
|
||||||
|
backwards
|
||||||
|
|
||||||
|
'''
|
||||||
|
self.flip_forwards = forwards
|
||||||
|
self.before_image = QPixmap.fromImage(image)
|
||||||
|
self.after_image = None
|
||||||
|
self.setGeometry(0, 0, image.width(), image.height())
|
||||||
|
self.setVisible(True)
|
||||||
|
|
||||||
|
def __call__(self, image, duration=0.5):
|
||||||
|
'''
|
||||||
|
Start the animation. You must have called :meth:`initialize` first.
|
||||||
|
|
||||||
|
:param duration: Animation duration in seconds.
|
||||||
|
|
||||||
|
'''
|
||||||
|
if self.running:
|
||||||
|
return
|
||||||
|
self.after_image = QPixmap.fromImage(image)
|
||||||
|
|
||||||
|
if self.flip_forwards:
|
||||||
|
self.animation.setStartValue(image.width())
|
||||||
|
self.animation.setEndValue(0)
|
||||||
|
t = self.before_image
|
||||||
|
self.before_image = self.after_image
|
||||||
|
self.after_image = t
|
||||||
|
self.animation.setEasingCurve(QEasingCurve(QEasingCurve.InExpo))
|
||||||
|
else:
|
||||||
|
self.animation.setStartValue(0)
|
||||||
|
self.animation.setEndValue(image.width())
|
||||||
|
self.animation.setEasingCurve(QEasingCurve(QEasingCurve.OutExpo))
|
||||||
|
|
||||||
|
self.animation.setDuration(duration * 1000)
|
||||||
|
self.animation.start()
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
def finished(self):
|
||||||
|
self.setVisible(False)
|
||||||
|
self.before_image = self.after_image = None
|
||||||
|
|
||||||
|
def paintEvent(self, ev):
|
||||||
|
if self.before_image is None:
|
||||||
|
return
|
||||||
|
canvas_size = self.rect()
|
||||||
|
p = QPainter(self)
|
||||||
|
p.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform)
|
||||||
|
|
||||||
|
p.drawPixmap(canvas_size, self.before_image,
|
||||||
|
self.before_image.rect())
|
||||||
|
if self.after_image is not None:
|
||||||
|
width = self._current_width
|
||||||
|
iw = self.after_image.width()
|
||||||
|
sh = min(self.after_image.height(), canvas_size.height())
|
||||||
|
|
||||||
|
if self.flip_forwards:
|
||||||
|
source = QRect(max(0, iw - width), 0, width, sh)
|
||||||
|
else:
|
||||||
|
source = QRect(0, 0, width, sh)
|
||||||
|
|
||||||
|
target = QRect(source)
|
||||||
|
target.moveLeft(0)
|
||||||
|
p.drawPixmap(target, self.after_image, source)
|
||||||
|
|
||||||
|
p.end()
|
||||||
|
|
||||||
|
def set_current_width(self, val):
|
||||||
|
self._current_width = val
|
||||||
|
|
||||||
|
current_width = pyqtProperty('int',
|
||||||
|
fget=lambda self: self._current_width,
|
||||||
|
fset=set_current_width
|
||||||
|
)
|
||||||
|
|
||||||
|
|
@ -5,6 +5,8 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
import gc
|
||||||
|
|
||||||
## {{{ http://code.activestate.com/recipes/286222/ (r1)
|
## {{{ http://code.activestate.com/recipes/286222/ (r1)
|
||||||
import os
|
import os
|
||||||
|
|
||||||
@ -52,4 +54,19 @@ def stacksize(since=0.0):
|
|||||||
## end of http://code.activestate.com/recipes/286222/ }}}
|
## end of http://code.activestate.com/recipes/286222/ }}}
|
||||||
|
|
||||||
|
|
||||||
|
def gc_histogram():
|
||||||
|
"""Returns per-class counts of existing objects."""
|
||||||
|
result = {}
|
||||||
|
for o in gc.get_objects():
|
||||||
|
t = type(o)
|
||||||
|
count = result.get(t, 0)
|
||||||
|
result[t] = count + 1
|
||||||
|
return result
|
||||||
|
|
||||||
|
def diff_hists(h1, h2):
|
||||||
|
"""Prints differences between two results of gc_histogram()."""
|
||||||
|
for k in h1:
|
||||||
|
if h1[k] != h2[k]:
|
||||||
|
print "%s: %d -> %d (%s%d)" % (
|
||||||
|
k, h1[k], h2[k], h2[k] > h1[k] and "+" or "", h2[k] - h1[k])
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user