From f50085d0388b08c15ef10295b74855bab8fb416b Mon Sep 17 00:00:00 2001 From: GRiker Date: Tue, 21 Sep 2010 05:40:33 -0700 Subject: [PATCH 1/3] GR tweaks to tweak-epub --- src/calibre/gui2/actions/tweak_epub.py | 3 +-- src/calibre/gui2/dialogs/tweak_epub.py | 3 --- src/calibre/gui2/dialogs/tweak_epub.ui | 8 ++++---- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/calibre/gui2/actions/tweak_epub.py b/src/calibre/gui2/actions/tweak_epub.py index 67ec34c12b..212aff8019 100755 --- a/src/calibre/gui2/actions/tweak_epub.py +++ b/src/calibre/gui2/actions/tweak_epub.py @@ -40,8 +40,7 @@ class TweakEpubAction(InterfaceAction): _('No ePub available. First convert the book to ePub.'), show=True) - - # Launch a modal dialog waiting for user to complete or cancel + # Launch modal dialog waiting for user to tweak or cancel dlg = TweakEpub(self.gui, path_to_epub) if dlg.exec_() == dlg.Accepted: self.update_db(book_id, dlg._output) diff --git a/src/calibre/gui2/dialogs/tweak_epub.py b/src/calibre/gui2/dialogs/tweak_epub.py index fb3643884b..db6e93fd7a 100755 --- a/src/calibre/gui2/dialogs/tweak_epub.py +++ b/src/calibre/gui2/dialogs/tweak_epub.py @@ -21,8 +21,6 @@ class TweakEpub(QDialog, Ui_Dialog): ''' Display controls for tweaking ePubs - To do: - - need way to kill file browser proc in cleanup() ''' def __init__(self, parent, epub): @@ -30,7 +28,6 @@ class TweakEpub(QDialog, Ui_Dialog): self._epub = epub self._exploded = None - #self._file_browser_proc = None self._output = None # Run the dialog setup generated from tweak_epub.ui diff --git a/src/calibre/gui2/dialogs/tweak_epub.ui b/src/calibre/gui2/dialogs/tweak_epub.ui index 9daa5a8f67..f841bd5eea 100644 --- a/src/calibre/gui2/dialogs/tweak_epub.ui +++ b/src/calibre/gui2/dialogs/tweak_epub.ui @@ -32,7 +32,7 @@ &Explode ePub - + :/images/wizard.png:/images/wizard.png @@ -49,7 +49,7 @@ &Rebuild ePub - + :/images/exec.png:/images/exec.png @@ -63,7 +63,7 @@ &Cancel - + :/images/window-close.png:/images/window-close.png @@ -71,7 +71,7 @@ - First, explode the epub. Then edit is contents by right clicking on the individual files and selecting the editor of your choice. When you are done, click rebuild epub and the epub in your calibre library will be updated with the changes you have made. + Explode the ePub to display contents in a file browser window. To tweak individual files, right-click, selecting 'Open with...' your editor of choice. When tweaks are complete, close the file browser window. Rebuild the ePub, updating your calibre library. true From 98f0851ebb3eb4a2711440935e10531e6bdea996 Mon Sep 17 00:00:00 2001 From: GRiker Date: Tue, 21 Sep 2010 05:47:22 -0700 Subject: [PATCH 2/3] GR tweaks to tweak-epub --- src/calibre/gui2/dialogs/tweak_epub.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/gui2/dialogs/tweak_epub.ui b/src/calibre/gui2/dialogs/tweak_epub.ui index f841bd5eea..ccd33f44ab 100644 --- a/src/calibre/gui2/dialogs/tweak_epub.ui +++ b/src/calibre/gui2/dialogs/tweak_epub.ui @@ -71,7 +71,7 @@ - Explode the ePub to display contents in a file browser window. To tweak individual files, right-click, selecting 'Open with...' your editor of choice. When tweaks are complete, close the file browser window. Rebuild the ePub, updating your calibre library. + Explode the ePub to display contents in a file browser window. To tweak individual files, right-click, then 'Open with...' your editor of choice. When tweaks are complete, close the file browser window. Rebuild the ePub, updating your calibre library. true From 52f85d3ef422c882cde953c57e72e8e3e5fe50ad Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 21 Sep 2010 11:56:09 -0600 Subject: [PATCH 3/3] Cover cache: load images only in the GUI thread to prevent stale files being leftover by set_path due to Windows file locking --- src/calibre/gui2/__init__.py | 30 +++++++++++++++++++++++++++++- src/calibre/gui2/library/models.py | 4 ++-- src/calibre/library/caches.py | 8 +++++--- 3 files changed, 36 insertions(+), 6 deletions(-) diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index c284900734..66e199b8a0 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -1,7 +1,7 @@ __license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal ' """ The GUI """ -import os, sys +import os, sys, Queue from threading import RLock from PyQt4.Qt import QVariant, QFileInfo, QObject, SIGNAL, QBuffer, Qt, \ @@ -296,6 +296,34 @@ class Dispatcher(QObject): def dispatch(self, args, kwargs): self.func(*args, **kwargs) +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. + ''' + dispatch_signal = pyqtSignal(object, object, object) + + def __init__(self, func, queued=True, parent=None): + QObject.__init__(self, parent) + self.func = func + typ = Qt.QueuedConnection + if not queued: + typ = Qt.AutoConnection if queued is None else Qt.DirectConnection + self.dispatch_signal.connect(self.dispatch, type=typ) + + def __call__(self, *args, **kwargs): + q = Queue.Queue() + self.dispatch_signal.emit(q, args, kwargs) + return q.get() + + def dispatch(self, q, args, kwargs): + try: + res = self.func(*args, **kwargs) + except: + res = None + q.put(res) + class GetMetadata(QObject): ''' Convenience class to ensure that metadata readers are used only in the diff --git a/src/calibre/gui2/library/models.py b/src/calibre/gui2/library/models.py index 3370fd4b75..53f701386b 100644 --- a/src/calibre/gui2/library/models.py +++ b/src/calibre/gui2/library/models.py @@ -12,7 +12,7 @@ from operator import attrgetter from PyQt4.Qt import QAbstractTableModel, Qt, pyqtSignal, QIcon, QImage, \ QModelIndex, QVariant, QDate -from calibre.gui2 import NONE, config, UNDEFINED_QDATE +from calibre.gui2 import NONE, config, UNDEFINED_QDATE, FunctionDispatcher from calibre.utils.pyparsing import ParseException from calibre.ebooks.metadata import fmt_sidx, authors_to_string, string_to_authors from calibre.ptempfile import PersistentTemporaryFile @@ -151,7 +151,7 @@ class BooksModel(QAbstractTableModel): # {{{ self.database_changed.emit(db) if self.cover_cache is not None: self.cover_cache.stop() - self.cover_cache = CoverCache(db) + self.cover_cache = CoverCache(db, FunctionDispatcher(self.db.cover)) self.cover_cache.start() def refresh_cover(event, ids): if event == 'cover' and self.cover_cache is not None: diff --git a/src/calibre/library/caches.py b/src/calibre/library/caches.py index 211baeb634..58edd89cb2 100644 --- a/src/calibre/library/caches.py +++ b/src/calibre/library/caches.py @@ -6,7 +6,7 @@ __license__ = 'GPL v3' __copyright__ = '2010, Kovid Goyal ' __docformat__ = 'restructuredtext en' -import re, itertools +import re, itertools, time from itertools import repeat from datetime import timedelta from threading import Thread, RLock @@ -23,10 +23,11 @@ from calibre import fit_image class CoverCache(Thread): - def __init__(self, db): + def __init__(self, db, cover_func): Thread.__init__(self) self.daemon = True self.db = db + self.cover_func = cover_func self.load_queue = Queue() self.keep_running = True self.cache = {} @@ -37,7 +38,8 @@ class CoverCache(Thread): self.keep_running = False def _image_for_id(self, id_): - img = self.db.cover(id_, index_is_id=True, as_image=True) + time.sleep(0.050) # Limit 20/second to not overwhelm the GUI + img = self.cover_func(id_, index_is_id=True, as_image=True) if img is None: img = QImage() if not img.isNull():