From 7a16626073d447411a8bf918752f07176c9545e0 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 11 Jun 2011 08:25:21 -0600 Subject: [PATCH 01/13] UK Daily Mirror by Dave Asbury --- recipes/daily_mirror.recipe | 52 +++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 recipes/daily_mirror.recipe diff --git a/recipes/daily_mirror.recipe b/recipes/daily_mirror.recipe new file mode 100644 index 0000000000..5d4dbe3f4b --- /dev/null +++ b/recipes/daily_mirror.recipe @@ -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') + ] + From fb107eb94a25ec66d5e800207cfbd2169c4f035b Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 11 Jun 2011 09:27:21 -0600 Subject: [PATCH 02/13] FB2 Input: Do not specify font families/background colors --- resources/templates/fb2.xsl | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/resources/templates/fb2.xsl b/resources/templates/fb2.xsl index 273edd71ae..5424c226d7 100644 --- a/resources/templates/fb2.xsl +++ b/resources/templates/fb2.xsl @@ -32,16 +32,11 @@ From 72d9c3fa96168260536fdb2223b1953074ceccb5 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 11 Jun 2011 09:50:34 -0600 Subject: [PATCH 03/13] Fix bundled PyCrypto in the windows build --- setup/installer/windows/freeze.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/setup/installer/windows/freeze.py b/setup/installer/windows/freeze.py index d901b51642..026cac99cf 100644 --- a/setup/installer/windows/freeze.py +++ b/setup/installer/windows/freeze.py @@ -179,6 +179,24 @@ class Win32Freeze(Command, WixMixIn): shutil.copytree(self.j(comext, 'shell'), self.j(sp_dir, 'win32com', 'shell')) 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', ): x = glob.glob(self.j(self.lib_dir, 'site-packages', pat))[0] shutil.rmtree(x) From 0338744cb8bb4b6609976220c1e8a08990158f0e Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sat, 11 Jun 2011 18:13:59 +0100 Subject: [PATCH 04/13] Use FunctionDispatcher instead of Dispatcher in device to improve job-to-job synchronization --- src/calibre/gui2/device.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/calibre/gui2/device.py b/src/calibre/gui2/device.py index dd9d7aaa50..eb35e937c0 100644 --- a/src/calibre/gui2/device.py +++ b/src/calibre/gui2/device.py @@ -17,7 +17,7 @@ from calibre.gui2.dialogs.choose_format_device import ChooseFormatDeviceDialog from calibre.utils.ipc.job import BaseJob from calibre.devices.scanner import DeviceScanner 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 import preferred_encoding, prints, force_unicode, as_unicode from calibre.utils.filenames import ascii_filename @@ -611,7 +611,7 @@ class DeviceMixin(object): # {{{ self.device_error_dialog = error_dialog(self, _('Error'), _('Error communicating with device'), ' ') 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), Dispatcher(self.show_open_feedback)) self.device_manager.start() @@ -736,7 +736,7 @@ class DeviceMixin(object): # {{{ self.set_device_menu_items_state(connected) if connected: self.device_manager.get_device_information(\ - Dispatcher(self.info_read)) + FunctionDispatcher(self.info_read)) self.set_default_thumbnail(\ self.device_manager.device.THUMBNAIL_HEIGHT) self.status_bar.show_message(_('Device: ')+\ @@ -767,7 +767,7 @@ class DeviceMixin(object): # {{{ self.device_manager.device.icon) self.bars_manager.update_bars() 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): ''' @@ -810,7 +810,7 @@ class DeviceMixin(object): # {{{ def remove_paths(self, paths): return self.device_manager.delete_books( - Dispatcher(self.books_deleted), paths) + FunctionDispatcher(self.books_deleted), paths) def books_deleted(self, job): ''' @@ -1187,7 +1187,7 @@ class DeviceMixin(object): # {{{ Upload metadata to device. ''' 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) def metadata_synced(self, job): @@ -1222,7 +1222,7 @@ class DeviceMixin(object): # {{{ titles = [i.title for i in metadata] plugboards = self.library_view.model().db.prefs.get('plugboards', {}) job = self.device_manager.upload_books( - Dispatcher(self.books_uploaded), + FunctionDispatcher(self.books_uploaded), files, names, on_card=on_card, metadata=metadata, titles=titles, plugboards=plugboards ) @@ -1475,7 +1475,7 @@ class DeviceMixin(object): # {{{ self.cover_to_thumbnail(open(book.cover, 'rb').read()) plugboards = self.library_view.model().db.prefs.get('plugboards', {}) self.device_manager.sync_booklists( - Dispatcher(self.metadata_synced), booklists, + FunctionDispatcher(self.metadata_synced), booklists, plugboards) return update_metadata # }}} From 7fccb95fc265b2de35818bdce9e527d4a29645be Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 11 Jun 2011 11:28:10 -0600 Subject: [PATCH 05/13] ... --- src/calibre/gui2/email.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/calibre/gui2/email.py b/src/calibre/gui2/email.py index c8adeb7d31..4b4c920a7e 100644 --- a/src/calibre/gui2/email.py +++ b/src/calibre/gui2/email.py @@ -11,8 +11,8 @@ from binascii import unhexlify from functools import partial from itertools import repeat -from calibre.utils.smtp import compose_mail, sendmail, extract_email_address, \ - config as email_config +from calibre.utils.smtp import (compose_mail, sendmail, extract_email_address, + config as email_config) from calibre.utils.filenames import ascii_filename from calibre.customize.ui import available_input_formats, available_output_formats from calibre.ebooks.metadata import authors_to_string @@ -67,8 +67,8 @@ class Sendmail(object): from_ = opts.from_ if not from_: from_ = 'calibre ' - msg = compose_mail(from_, to, text, subject, open(attachment, 'rb'), - aname) + with lopen(attachment, 'rb') as f: + msg = compose_mail(from_, to, text, subject, f, aname) efrom, eto = map(extract_email_address, (from_, to)) eto = [eto] sendmail(msg, efrom, eto, localhost=None, From 07b12774b472e5e3b64a2b2794d6ab177b3e41e7 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sat, 11 Jun 2011 20:53:01 +0100 Subject: [PATCH 06/13] Fix problem in FunctionDispatcher where it hangs if calling thread == called thread. --- src/calibre/gui2/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index 8499e304c3..ae25dead4e 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -305,8 +305,11 @@ class FunctionDispatcher(QObject): self.dispatch_signal.connect(self.dispatch, type=typ) self.q = Queue.Queue() self.lock = threading.Lock() + self.calling_thread = QThread.currentThread() def __call__(self, *args, **kwargs): + if self.calling_thread == QThread.currentThread(): + return self.func(*args, **kwargs) with self.lock: self.dispatch_signal.emit(self.q, args, kwargs) res = self.q.get() From f80fdd9dd0e0a566b133d2223ad633fff926d597 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 11 Jun 2011 13:56:07 -0600 Subject: [PATCH 07/13] Revert device jobs changes --- src/calibre/gui2/device.py | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/src/calibre/gui2/device.py b/src/calibre/gui2/device.py index 268b67d22e..dd9d7aaa50 100644 --- a/src/calibre/gui2/device.py +++ b/src/calibre/gui2/device.py @@ -6,18 +6,18 @@ __copyright__ = '2008, Kovid Goyal ' import os, traceback, Queue, time, cStringIO, re, sys from threading import Thread -from PyQt4.Qt import (QMenu, QAction, QActionGroup, QIcon, SIGNAL, - Qt, pyqtSignal, QDialog, QObject) +from PyQt4.Qt import QMenu, QAction, QActionGroup, QIcon, SIGNAL, \ + Qt, pyqtSignal, QDialog, QObject -from calibre.customize.ui import (available_input_formats, available_output_formats, - device_plugins) +from calibre.customize.ui import available_input_formats, available_output_formats, \ + device_plugins from calibre.devices.interface import DevicePlugin from calibre.devices.errors import UserFeedback, OpenFeedback from calibre.gui2.dialogs.choose_format_device import ChooseFormatDeviceDialog from calibre.utils.ipc.job import BaseJob from calibre.devices.scanner import DeviceScanner -from calibre.gui2 import (config, error_dialog, Dispatcher, dynamic, - warning_dialog, info_dialog, choose_dir, FunctionDispatcher) +from calibre.gui2 import config, error_dialog, Dispatcher, dynamic, \ + warning_dialog, info_dialog, choose_dir from calibre.ebooks.metadata import authors_to_string from calibre import preferred_encoding, prints, force_unicode, as_unicode from calibre.utils.filenames import ascii_filename @@ -611,7 +611,7 @@ class DeviceMixin(object): # {{{ self.device_error_dialog = error_dialog(self, _('Error'), _('Error communicating with device'), ' ') self.device_error_dialog.setModal(Qt.NonModal) - self.device_manager = DeviceManager(FunctionDispatcher(self.device_detected), + self.device_manager = DeviceManager(Dispatcher(self.device_detected), self.job_manager, Dispatcher(self.status_bar.show_message), Dispatcher(self.show_open_feedback)) self.device_manager.start() @@ -729,16 +729,14 @@ class DeviceMixin(object): # {{{ ''' Called when a device is connected to the computer. ''' - # This could happen historically as this function was called in a queued connection and - # the user could have yanked the device in the meantime. Now the device - # thread is blocked while it is called, but the check is harmless, so - # we leave it in. + # This can happen as this function is called in a queued connection and + # the user could have yanked the device in the meantime if connected and not self.device_manager.is_device_connected: connected = False self.set_device_menu_items_state(connected) if connected: self.device_manager.get_device_information(\ - FunctionDispatcher(self.info_read)) + Dispatcher(self.info_read)) self.set_default_thumbnail(\ self.device_manager.device.THUMBNAIL_HEIGHT) self.status_bar.show_message(_('Device: ')+\ @@ -746,7 +744,7 @@ class DeviceMixin(object): # {{{ _(' detected.'), 3000) self.device_connected = device_kind self.library_view.set_device_connected(self.device_connected) - self.refresh_ondevice(reset_only=True) + self.refresh_ondevice (reset_only = True) else: self.device_connected = None self.status_bar.device_disconnected() @@ -769,7 +767,7 @@ class DeviceMixin(object): # {{{ self.device_manager.device.icon) self.bars_manager.update_bars() self.status_bar.device_connected(info[0]) - self.device_manager.books(FunctionDispatcher(self.metadata_downloaded)) + self.device_manager.books(Dispatcher(self.metadata_downloaded)) def metadata_downloaded(self, job): ''' @@ -812,7 +810,7 @@ class DeviceMixin(object): # {{{ def remove_paths(self, paths): return self.device_manager.delete_books( - FunctionDispatcher(self.books_deleted), paths) + Dispatcher(self.books_deleted), paths) def books_deleted(self, job): ''' @@ -1189,7 +1187,7 @@ class DeviceMixin(object): # {{{ Upload metadata to device. ''' plugboards = self.library_view.model().db.prefs.get('plugboards', {}) - self.device_manager.sync_booklists(FunctionDispatcher(self.metadata_synced), + self.device_manager.sync_booklists(Dispatcher(self.metadata_synced), self.booklists(), plugboards) def metadata_synced(self, job): @@ -1224,7 +1222,7 @@ class DeviceMixin(object): # {{{ titles = [i.title for i in metadata] plugboards = self.library_view.model().db.prefs.get('plugboards', {}) job = self.device_manager.upload_books( - FunctionDispatcher(self.books_uploaded), + Dispatcher(self.books_uploaded), files, names, on_card=on_card, metadata=metadata, titles=titles, plugboards=plugboards ) @@ -1477,7 +1475,7 @@ class DeviceMixin(object): # {{{ self.cover_to_thumbnail(open(book.cover, 'rb').read()) plugboards = self.library_view.model().db.prefs.get('plugboards', {}) self.device_manager.sync_booklists( - FunctionDispatcher(self.metadata_synced), booklists, + Dispatcher(self.metadata_synced), booklists, plugboards) return update_metadata # }}} From 183fa08138a46526ddf56895d006e8256670d42f Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 11 Jun 2011 14:02:46 -0600 Subject: [PATCH 08/13] ... --- src/calibre/gui2/device.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/calibre/gui2/device.py b/src/calibre/gui2/device.py index dd9d7aaa50..97cb725f7e 100644 --- a/src/calibre/gui2/device.py +++ b/src/calibre/gui2/device.py @@ -6,18 +6,18 @@ __copyright__ = '2008, Kovid Goyal ' import os, traceback, Queue, time, cStringIO, re, sys from threading import Thread -from PyQt4.Qt import QMenu, QAction, QActionGroup, QIcon, SIGNAL, \ - Qt, pyqtSignal, QDialog, QObject +from PyQt4.Qt import (QMenu, QAction, QActionGroup, QIcon, SIGNAL, + Qt, pyqtSignal, QDialog, QObject) -from calibre.customize.ui import available_input_formats, available_output_formats, \ - device_plugins +from calibre.customize.ui import (available_input_formats, available_output_formats, + device_plugins) from calibre.devices.interface import DevicePlugin from calibre.devices.errors import UserFeedback, OpenFeedback from calibre.gui2.dialogs.choose_format_device import ChooseFormatDeviceDialog from calibre.utils.ipc.job import BaseJob from calibre.devices.scanner import DeviceScanner -from calibre.gui2 import config, error_dialog, Dispatcher, dynamic, \ - warning_dialog, info_dialog, choose_dir +from calibre.gui2 import (config, error_dialog, Dispatcher, dynamic, + warning_dialog, info_dialog, choose_dir) from calibre.ebooks.metadata import authors_to_string from calibre import preferred_encoding, prints, force_unicode, as_unicode from calibre.utils.filenames import ascii_filename From b1979948b591cc865e2781bae1759e41b6d3a84b Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 11 Jun 2011 15:19:53 -0600 Subject: [PATCH 09/13] ... --- src/calibre/gui2/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index c217d90ddf..8461896d54 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -297,6 +297,7 @@ class FunctionDispatcher(QObject): dispatch_signal = pyqtSignal(object, object, object) def __init__(self, func, queued=True, parent=None): + global gui_thread QObject.__init__(self, parent) self.func = func typ = Qt.QueuedConnection @@ -305,6 +306,8 @@ class FunctionDispatcher(QObject): self.dispatch_signal.connect(self.dispatch, type=typ) self.q = Queue.Queue() self.lock = threading.Lock() + if gui_thread is None: + gui_thread = QThread.currentThread() def __call__(self, *args, **kwargs): if is_gui_thread(): From e819fced24cc73f954f851acf55aa580af92eda4 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 11 Jun 2011 15:40:45 -0600 Subject: [PATCH 10/13] ... --- src/calibre/gui2/__init__.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index 8461896d54..3ea6e70050 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -298,6 +298,12 @@ class FunctionDispatcher(QObject): 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) self.func = func typ = Qt.QueuedConnection @@ -306,8 +312,6 @@ class FunctionDispatcher(QObject): self.dispatch_signal.connect(self.dispatch, type=typ) self.q = Queue.Queue() self.lock = threading.Lock() - if gui_thread is None: - gui_thread = QThread.currentThread() def __call__(self, *args, **kwargs): if is_gui_thread(): From 8de93d834df68c711697dfd600b5c66fe996dcc6 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 11 Jun 2011 15:45:51 -0600 Subject: [PATCH 11/13] ... --- src/calibre/gui2/__init__.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index 3ea6e70050..f69788e849 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -271,6 +271,9 @@ class Dispatcher(QObject): Convenience class to use Qt signals with arbitrary python callables. By default, ensures that a function call always happens in the 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) @@ -292,7 +295,9 @@ class FunctionDispatcher(QObject): ''' Convenience class to use Qt signals with arbitrary python functions. 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) From 2b4e1d8608db4c7f0b76d4b6f2ecb16f1135457b Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 11 Jun 2011 16:20:12 -0600 Subject: [PATCH 12/13] Device jobs: Ensure that the job done callback is always called before the next queued device is started --- src/calibre/gui2/device.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/calibre/gui2/device.py b/src/calibre/gui2/device.py index 97cb725f7e..ab02a183c6 100644 --- a/src/calibre/gui2/device.py +++ b/src/calibre/gui2/device.py @@ -17,7 +17,7 @@ from calibre.gui2.dialogs.choose_format_device import ChooseFormatDeviceDialog from calibre.utils.ipc.job import BaseJob from calibre.devices.scanner import DeviceScanner 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 import preferred_encoding, prints, force_unicode, as_unicode from calibre.utils.filenames import ascii_filename @@ -35,8 +35,9 @@ class DeviceJob(BaseJob): # {{{ def __init__(self, func, done, job_manager, args=[], kwargs={}, description=''): - BaseJob.__init__(self, description, done=done) + BaseJob.__init__(self, description) self.func = func + self.callback_on_done = done self.args, self.kwargs = args, kwargs self.exception = None self.job_manager = job_manager @@ -50,6 +51,10 @@ class DeviceJob(BaseJob): # {{{ def job_done(self): self.duration = time.time() - self.start_time self.percent = 1 + try: + self.callback_on_done(self) + except: + pass self.job_manager.changed_queue.put(self) def report_progress(self, percent, msg=''): @@ -611,7 +616,7 @@ class DeviceMixin(object): # {{{ self.device_error_dialog = error_dialog(self, _('Error'), _('Error communicating with device'), ' ') 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), Dispatcher(self.show_open_feedback)) self.device_manager.start() @@ -736,7 +741,7 @@ class DeviceMixin(object): # {{{ self.set_device_menu_items_state(connected) if connected: self.device_manager.get_device_information(\ - Dispatcher(self.info_read)) + FunctionDispatcher(self.info_read)) self.set_default_thumbnail(\ self.device_manager.device.THUMBNAIL_HEIGHT) self.status_bar.show_message(_('Device: ')+\ @@ -767,7 +772,7 @@ class DeviceMixin(object): # {{{ self.device_manager.device.icon) self.bars_manager.update_bars() 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): ''' @@ -810,7 +815,7 @@ class DeviceMixin(object): # {{{ def remove_paths(self, paths): return self.device_manager.delete_books( - Dispatcher(self.books_deleted), paths) + FunctionDispatcher(self.books_deleted), paths) def books_deleted(self, job): ''' @@ -1187,7 +1192,7 @@ class DeviceMixin(object): # {{{ Upload metadata to device. ''' 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) def metadata_synced(self, job): @@ -1222,7 +1227,7 @@ class DeviceMixin(object): # {{{ titles = [i.title for i in metadata] plugboards = self.library_view.model().db.prefs.get('plugboards', {}) job = self.device_manager.upload_books( - Dispatcher(self.books_uploaded), + FunctionDispatcher(self.books_uploaded), files, names, on_card=on_card, metadata=metadata, titles=titles, plugboards=plugboards ) @@ -1475,7 +1480,7 @@ class DeviceMixin(object): # {{{ self.cover_to_thumbnail(open(book.cover, 'rb').read()) plugboards = self.library_view.model().db.prefs.get('plugboards', {}) self.device_manager.sync_booklists( - Dispatcher(self.metadata_synced), booklists, + FunctionDispatcher(self.metadata_synced), booklists, plugboards) return update_metadata # }}} From ee04420a2e030cb4b5ac58471bed56ecc69d5ceb Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 12 Jun 2011 08:22:52 -0600 Subject: [PATCH 13/13] ... --- src/calibre/gui2/device.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/calibre/gui2/device.py b/src/calibre/gui2/device.py index ab02a183c6..9f71c3088d 100644 --- a/src/calibre/gui2/device.py +++ b/src/calibre/gui2/device.py @@ -38,6 +38,10 @@ class DeviceJob(BaseJob): # {{{ BaseJob.__init__(self, description) 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.exception = None self.job_manager = job_manager @@ -259,7 +263,8 @@ class DeviceManager(Thread): # {{{ job = self.next() if job is not None: 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 = None else: