merge from trunk

This commit is contained in:
Lee 2011-06-13 02:49:19 +10:00
commit 67b12111e2
6 changed files with 116 additions and 29 deletions

View File

@ -0,0 +1,52 @@
from calibre.web.feeds.news import BasicNewsRecipe
class AdvancedUserRecipe1306061239(BasicNewsRecipe):
title = u'The Daily Mirror'
description = 'News as provide by The Daily Mirror -UK'
__author__ = 'Dave Asbury'
language = 'en_GB'
cover_url = 'http://yookeo.com/screens/m/i/mirror.co.uk.jpg'
masthead_url = 'http://www.nmauk.co.uk/nma/images/daily_mirror.gif'
oldest_article = 1
max_articles_per_feed = 100
remove_empty_feeds = True
remove_javascript = True
no_stylesheets = True
keep_only_tags = [
dict(name='h1'),
dict(attrs={'class':['article-attr']}),
dict(name='div', attrs={'class' : [ 'article-body', 'crosshead']})
]
remove_tags = [
dict(name='div', attrs={'class' : ['caption', 'article-resize']}),
dict( attrs={'class':'append-html'})
]
feeds = [
(u'News', u'http://www.mirror.co.uk/news/rss.xml')
,(u'Tech News', u'http://www.mirror.co.uk/news/technology/rss.xml')
,(u'Weird World','http://www.mirror.co.uk/news/weird-world/rss.xml')
,(u'Film Gossip','http://www.mirror.co.uk/celebs/film/rss.xml')
,(u'Music News','http://www.mirror.co.uk/celebs/music/rss.xml')
,(u'Celebs and Tv Gossip','http://www.mirror.co.uk/celebs/tv/rss.xml')
,(u'Sport','http://www.mirror.co.uk/sport/rss.xml')
,(u'Life Style','http://www.mirror.co.uk/life-style/rss.xml')
,(u'Advice','http://www.mirror.co.uk/advice/rss.xml')
,(u'Travel','http://www.mirror.co.uk/advice/travel/rss.xml')
# example of commented out feed not needed ,(u'Travel','http://www.mirror.co.uk/advice/travel/rss.xml')
]

View File

@ -32,16 +32,11 @@
<xsl:value-of select="fb:description/fb:title-info/fb:book-title"/> <xsl:value-of select="fb:description/fb:title-info/fb:book-title"/>
</title> </title>
<style type="text/css"> <style type="text/css">
a { color : #0002CC } body { text-align : justify }
a:hover { color : #BF0000 }
body { background-color : #FEFEFE; color : #000000; font-family : Verdana, Geneva, Arial, Helvetica, sans-serif; text-align : justify }
h1{ font-size : 160%; font-style : normal; font-weight : bold; text-align : left; border : 1px solid Black; background-color : #E7E7E7; margin-left : 0px; page-break-before : always; } h1{ font-size : 160%; font-style : normal; font-weight : bold; text-align : left; border : 1px solid Black; background-color : #E7E7E7; margin-left : 0px; page-break-before : always; }
h2{ font-size : 130%; font-style : normal; font-weight : bold; text-align : left; background-color : #EEEEEE; border : 1px solid Gray; page-break-before : always; } h2{ font-size : 130%; font-style : normal; font-weight : bold; text-align : left; background-color : #EEEEEE; border : 1px solid Gray; page-break-before : always; }
h3{ font-size : 110%; font-style : normal; font-weight : bold; text-align : left; background-color : #F1F1F1; border : 1px solid Silver;} h3{ font-size : 110%; font-style : normal; font-weight : bold; text-align : left; background-color : #F1F1F1; border : 1px solid Silver;}
h4{ font-size : 100%; font-style : normal; font-weight : bold; text-align : left; border : 1px solid Gray; background-color : #F4F4F4;} h4{ font-size : 100%; font-style : normal; font-weight : bold; text-align : left; border : 1px solid Gray; background-color : #F4F4F4;}
@ -56,13 +51,11 @@
hr { color : Black } hr { color : Black }
div {font-family : "Times New Roman", Times, serif; text-align : justify}
ul {margin-left: 0} ul {margin-left: 0}
.epigraph{width:50%; margin-left : 35%;} .epigraph{width:50%; margin-left : 35%;}
div.paragraph { text-align: justify; text-indent: 2em; } div.paragraph { text-indent: 2em; }
</style> </style>
<link rel="stylesheet" type="text/css" href="inline-styles.css" /> <link rel="stylesheet" type="text/css" href="inline-styles.css" />
</head> </head>

View File

@ -179,6 +179,24 @@ class Win32Freeze(Command, WixMixIn):
shutil.copytree(self.j(comext, 'shell'), self.j(sp_dir, 'win32com', 'shell')) shutil.copytree(self.j(comext, 'shell'), self.j(sp_dir, 'win32com', 'shell'))
shutil.rmtree(comext) shutil.rmtree(comext)
# Fix PyCrypto, removing the bootstrap .py modules that load the .pyd
# modules, since they do not work when in a zip file
for crypto_dir in glob.glob(self.j(sp_dir, 'pycrypto-*', 'Crypto')):
for dirpath, dirnames, filenames in os.walk(crypto_dir):
for f in filenames:
name, ext = os.path.splitext(f)
if ext == '.pyd':
with open(self.j(dirpath, name+'.py')) as f:
raw = f.read().strip()
if (not raw.startswith('def __bootstrap__') or not
raw.endswith('__bootstrap__()')):
raise Exception('The PyCrypto file %r has non'
' bootstrap code'%self.j(dirpath, f))
for ext in ('.py', '.pyc', '.pyo'):
x = self.j(dirpath, name+ext)
if os.path.exists(x):
os.remove(x)
for pat in (r'PyQt4\uic\port_v3', ): for pat in (r'PyQt4\uic\port_v3', ):
x = glob.glob(self.j(self.lib_dir, 'site-packages', pat))[0] x = glob.glob(self.j(self.lib_dir, 'site-packages', pat))[0]
shutil.rmtree(x) shutil.rmtree(x)

View File

@ -271,6 +271,9 @@ class Dispatcher(QObject):
Convenience class to use Qt signals with arbitrary python callables. Convenience class to use Qt signals with arbitrary python callables.
By default, ensures that a function call always happens in the By default, ensures that a function call always happens in the
thread this Dispatcher was created in. thread this Dispatcher was created in.
Note that if you create the Dispatcher in a thread without an event loop of
its own, the function call will happen in the GUI thread (I think).
''' '''
dispatch_signal = pyqtSignal(object, object) dispatch_signal = pyqtSignal(object, object)
@ -292,11 +295,20 @@ class FunctionDispatcher(QObject):
''' '''
Convenience class to use Qt signals with arbitrary python functions. Convenience class to use Qt signals with arbitrary python functions.
By default, ensures that a function call always happens in the By default, ensures that a function call always happens in the
thread this Dispatcher was created in. thread this FunctionDispatcher was created in.
Note that you must create FunctionDispatcher objects in the GUI thread.
''' '''
dispatch_signal = pyqtSignal(object, object, object) dispatch_signal = pyqtSignal(object, object, object)
def __init__(self, func, queued=True, parent=None): def __init__(self, func, queued=True, parent=None):
global gui_thread
if gui_thread is None:
gui_thread = QThread.currentThread()
if not is_gui_thread():
raise ValueError(
'You can only create a FunctionDispatcher in the GUI thread')
QObject.__init__(self, parent) QObject.__init__(self, parent)
self.func = func self.func = func
typ = Qt.QueuedConnection typ = Qt.QueuedConnection
@ -307,6 +319,8 @@ class FunctionDispatcher(QObject):
self.lock = threading.Lock() self.lock = threading.Lock()
def __call__(self, *args, **kwargs): def __call__(self, *args, **kwargs):
if is_gui_thread():
return self.func(*args, **kwargs)
with self.lock: with self.lock:
self.dispatch_signal.emit(self.q, args, kwargs) self.dispatch_signal.emit(self.q, args, kwargs)
res = self.q.get() res = self.q.get()

View File

@ -6,18 +6,18 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
import os, traceback, Queue, time, cStringIO, re, sys import os, traceback, Queue, time, cStringIO, re, sys
from threading import Thread from threading import Thread
from PyQt4.Qt import QMenu, QAction, QActionGroup, QIcon, SIGNAL, \ from PyQt4.Qt import (QMenu, QAction, QActionGroup, QIcon, SIGNAL,
Qt, pyqtSignal, QDialog, QObject Qt, pyqtSignal, QDialog, QObject)
from calibre.customize.ui import available_input_formats, available_output_formats, \ from calibre.customize.ui import (available_input_formats, available_output_formats,
device_plugins device_plugins)
from calibre.devices.interface import DevicePlugin from calibre.devices.interface import DevicePlugin
from calibre.devices.errors import UserFeedback, OpenFeedback from calibre.devices.errors import UserFeedback, OpenFeedback
from calibre.gui2.dialogs.choose_format_device import ChooseFormatDeviceDialog from calibre.gui2.dialogs.choose_format_device import ChooseFormatDeviceDialog
from calibre.utils.ipc.job import BaseJob from calibre.utils.ipc.job import BaseJob
from calibre.devices.scanner import DeviceScanner from calibre.devices.scanner import DeviceScanner
from calibre.gui2 import config, error_dialog, Dispatcher, dynamic, \ from calibre.gui2 import (config, error_dialog, Dispatcher, dynamic,
warning_dialog, info_dialog, choose_dir warning_dialog, info_dialog, choose_dir, FunctionDispatcher)
from calibre.ebooks.metadata import authors_to_string from calibre.ebooks.metadata import authors_to_string
from calibre import preferred_encoding, prints, force_unicode, as_unicode from calibre import preferred_encoding, prints, force_unicode, as_unicode
from calibre.utils.filenames import ascii_filename from calibre.utils.filenames import ascii_filename
@ -35,8 +35,13 @@ class DeviceJob(BaseJob): # {{{
def __init__(self, func, done, job_manager, args=[], kwargs={}, def __init__(self, func, done, job_manager, args=[], kwargs={},
description=''): description=''):
BaseJob.__init__(self, description, done=done) BaseJob.__init__(self, description)
self.func = func self.func = func
self.callback_on_done = done
if not isinstance(self.callback_on_done, (Dispatcher,
FunctionDispatcher)):
self.callback_on_done = FunctionDispatcher(self.callback_on_done)
self.args, self.kwargs = args, kwargs self.args, self.kwargs = args, kwargs
self.exception = None self.exception = None
self.job_manager = job_manager self.job_manager = job_manager
@ -50,6 +55,10 @@ class DeviceJob(BaseJob): # {{{
def job_done(self): def job_done(self):
self.duration = time.time() - self.start_time self.duration = time.time() - self.start_time
self.percent = 1 self.percent = 1
try:
self.callback_on_done(self)
except:
pass
self.job_manager.changed_queue.put(self) self.job_manager.changed_queue.put(self)
def report_progress(self, percent, msg=''): def report_progress(self, percent, msg=''):
@ -254,7 +263,8 @@ class DeviceManager(Thread): # {{{
job = self.next() job = self.next()
if job is not None: if job is not None:
self.current_job = job self.current_job = job
self.device.set_progress_reporter(job.report_progress) if self.device is not None:
self.device.set_progress_reporter(job.report_progress)
self.current_job.run() self.current_job.run()
self.current_job = None self.current_job = None
else: else:
@ -611,7 +621,7 @@ class DeviceMixin(object): # {{{
self.device_error_dialog = error_dialog(self, _('Error'), self.device_error_dialog = error_dialog(self, _('Error'),
_('Error communicating with device'), ' ') _('Error communicating with device'), ' ')
self.device_error_dialog.setModal(Qt.NonModal) self.device_error_dialog.setModal(Qt.NonModal)
self.device_manager = DeviceManager(Dispatcher(self.device_detected), self.device_manager = DeviceManager(FunctionDispatcher(self.device_detected),
self.job_manager, Dispatcher(self.status_bar.show_message), self.job_manager, Dispatcher(self.status_bar.show_message),
Dispatcher(self.show_open_feedback)) Dispatcher(self.show_open_feedback))
self.device_manager.start() self.device_manager.start()
@ -736,7 +746,7 @@ class DeviceMixin(object): # {{{
self.set_device_menu_items_state(connected) self.set_device_menu_items_state(connected)
if connected: if connected:
self.device_manager.get_device_information(\ self.device_manager.get_device_information(\
Dispatcher(self.info_read)) FunctionDispatcher(self.info_read))
self.set_default_thumbnail(\ self.set_default_thumbnail(\
self.device_manager.device.THUMBNAIL_HEIGHT) self.device_manager.device.THUMBNAIL_HEIGHT)
self.status_bar.show_message(_('Device: ')+\ self.status_bar.show_message(_('Device: ')+\
@ -767,7 +777,7 @@ class DeviceMixin(object): # {{{
self.device_manager.device.icon) self.device_manager.device.icon)
self.bars_manager.update_bars() self.bars_manager.update_bars()
self.status_bar.device_connected(info[0]) self.status_bar.device_connected(info[0])
self.device_manager.books(Dispatcher(self.metadata_downloaded)) self.device_manager.books(FunctionDispatcher(self.metadata_downloaded))
def metadata_downloaded(self, job): def metadata_downloaded(self, job):
''' '''
@ -810,7 +820,7 @@ class DeviceMixin(object): # {{{
def remove_paths(self, paths): def remove_paths(self, paths):
return self.device_manager.delete_books( return self.device_manager.delete_books(
Dispatcher(self.books_deleted), paths) FunctionDispatcher(self.books_deleted), paths)
def books_deleted(self, job): def books_deleted(self, job):
''' '''
@ -1187,7 +1197,7 @@ class DeviceMixin(object): # {{{
Upload metadata to device. Upload metadata to device.
''' '''
plugboards = self.library_view.model().db.prefs.get('plugboards', {}) plugboards = self.library_view.model().db.prefs.get('plugboards', {})
self.device_manager.sync_booklists(Dispatcher(self.metadata_synced), self.device_manager.sync_booklists(FunctionDispatcher(self.metadata_synced),
self.booklists(), plugboards) self.booklists(), plugboards)
def metadata_synced(self, job): def metadata_synced(self, job):
@ -1222,7 +1232,7 @@ class DeviceMixin(object): # {{{
titles = [i.title for i in metadata] titles = [i.title for i in metadata]
plugboards = self.library_view.model().db.prefs.get('plugboards', {}) plugboards = self.library_view.model().db.prefs.get('plugboards', {})
job = self.device_manager.upload_books( job = self.device_manager.upload_books(
Dispatcher(self.books_uploaded), FunctionDispatcher(self.books_uploaded),
files, names, on_card=on_card, files, names, on_card=on_card,
metadata=metadata, titles=titles, plugboards=plugboards metadata=metadata, titles=titles, plugboards=plugboards
) )
@ -1475,7 +1485,7 @@ class DeviceMixin(object): # {{{
self.cover_to_thumbnail(open(book.cover, 'rb').read()) self.cover_to_thumbnail(open(book.cover, 'rb').read())
plugboards = self.library_view.model().db.prefs.get('plugboards', {}) plugboards = self.library_view.model().db.prefs.get('plugboards', {})
self.device_manager.sync_booklists( self.device_manager.sync_booklists(
Dispatcher(self.metadata_synced), booklists, FunctionDispatcher(self.metadata_synced), booklists,
plugboards) plugboards)
return update_metadata return update_metadata
# }}} # }}}

View File

@ -11,8 +11,8 @@ from binascii import unhexlify
from functools import partial from functools import partial
from itertools import repeat from itertools import repeat
from calibre.utils.smtp import compose_mail, sendmail, extract_email_address, \ from calibre.utils.smtp import (compose_mail, sendmail, extract_email_address,
config as email_config config as email_config)
from calibre.utils.filenames import ascii_filename from calibre.utils.filenames import ascii_filename
from calibre.customize.ui import available_input_formats, available_output_formats from calibre.customize.ui import available_input_formats, available_output_formats
from calibre.ebooks.metadata import authors_to_string from calibre.ebooks.metadata import authors_to_string
@ -67,8 +67,8 @@ class Sendmail(object):
from_ = opts.from_ from_ = opts.from_
if not from_: if not from_:
from_ = 'calibre <calibre@'+socket.getfqdn()+'>' from_ = 'calibre <calibre@'+socket.getfqdn()+'>'
msg = compose_mail(from_, to, text, subject, open(attachment, 'rb'), with lopen(attachment, 'rb') as f:
aname) msg = compose_mail(from_, to, text, subject, f, aname)
efrom, eto = map(extract_email_address, (from_, to)) efrom, eto = map(extract_email_address, (from_, to))
eto = [eto] eto = [eto]
sendmail(msg, efrom, eto, localhost=None, sendmail(msg, efrom, eto, localhost=None,