mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Disable the cover cache. This means that if you are running calibre on an underpowered machine, you might notice some slow down in the cover browser. On the other hand, calibre's memory consumption will be reduced.
This commit is contained in:
parent
818c4249d2
commit
05ed274726
@ -12,7 +12,7 @@ from operator import attrgetter
|
|||||||
from PyQt4.Qt import QAbstractTableModel, Qt, pyqtSignal, QIcon, QImage, \
|
from PyQt4.Qt import QAbstractTableModel, Qt, pyqtSignal, QIcon, QImage, \
|
||||||
QModelIndex, QVariant, QDate
|
QModelIndex, QVariant, QDate
|
||||||
|
|
||||||
from calibre.gui2 import NONE, config, UNDEFINED_QDATE, FunctionDispatcher
|
from calibre.gui2 import NONE, config, UNDEFINED_QDATE
|
||||||
from calibre.utils.pyparsing import ParseException
|
from calibre.utils.pyparsing import ParseException
|
||||||
from calibre.ebooks.metadata import fmt_sidx, authors_to_string, string_to_authors
|
from calibre.ebooks.metadata import fmt_sidx, authors_to_string, string_to_authors
|
||||||
from calibre.ptempfile import PersistentTemporaryFile
|
from calibre.ptempfile import PersistentTemporaryFile
|
||||||
@ -22,7 +22,7 @@ from calibre.utils.icu import sort_key, strcmp as icu_strcmp
|
|||||||
from calibre.ebooks.metadata.meta import set_metadata as _set_metadata
|
from calibre.ebooks.metadata.meta import set_metadata as _set_metadata
|
||||||
from calibre.utils.search_query_parser import SearchQueryParser
|
from calibre.utils.search_query_parser import SearchQueryParser
|
||||||
from calibre.library.caches import _match, CONTAINS_MATCH, EQUALS_MATCH, \
|
from calibre.library.caches import _match, CONTAINS_MATCH, EQUALS_MATCH, \
|
||||||
REGEXP_MATCH, CoverCache, MetadataBackup
|
REGEXP_MATCH, MetadataBackup
|
||||||
from calibre.library.cli import parse_series_string
|
from calibre.library.cli import parse_series_string
|
||||||
from calibre import strftime, isbytestring, prepare_string_for_xml
|
from calibre import strftime, isbytestring, prepare_string_for_xml
|
||||||
from calibre.constants import filesystem_encoding, DEBUG
|
from calibre.constants import filesystem_encoding, DEBUG
|
||||||
@ -89,7 +89,6 @@ class BooksModel(QAbstractTableModel): # {{{
|
|||||||
self.headers = {}
|
self.headers = {}
|
||||||
self.alignment_map = {}
|
self.alignment_map = {}
|
||||||
self.buffer_size = buffer
|
self.buffer_size = buffer
|
||||||
self.cover_cache = None
|
|
||||||
self.metadata_backup = None
|
self.metadata_backup = None
|
||||||
self.bool_yes_icon = QIcon(I('ok.png'))
|
self.bool_yes_icon = QIcon(I('ok.png'))
|
||||||
self.bool_no_icon = QIcon(I('list_remove.png'))
|
self.bool_no_icon = QIcon(I('list_remove.png'))
|
||||||
@ -113,10 +112,6 @@ class BooksModel(QAbstractTableModel): # {{{
|
|||||||
def is_custom_column(self, cc_label):
|
def is_custom_column(self, cc_label):
|
||||||
return cc_label in self.custom_columns
|
return cc_label in self.custom_columns
|
||||||
|
|
||||||
def clear_caches(self):
|
|
||||||
if self.cover_cache:
|
|
||||||
self.cover_cache.clear_cache()
|
|
||||||
|
|
||||||
def read_config(self):
|
def read_config(self):
|
||||||
self.use_roman_numbers = config['use_roman_numerals_for_series_number']
|
self.use_roman_numbers = config['use_roman_numerals_for_series_number']
|
||||||
|
|
||||||
@ -154,18 +149,8 @@ class BooksModel(QAbstractTableModel): # {{{
|
|||||||
self.build_data_convertors()
|
self.build_data_convertors()
|
||||||
self.reset()
|
self.reset()
|
||||||
self.database_changed.emit(db)
|
self.database_changed.emit(db)
|
||||||
if self.cover_cache is not None:
|
|
||||||
self.cover_cache.stop()
|
|
||||||
# Would like to to a join here, but the thread might be waiting to
|
|
||||||
# do something on the GUI thread. Deadlock.
|
|
||||||
self.cover_cache = CoverCache(db, FunctionDispatcher(self.db.cover))
|
|
||||||
self.cover_cache.start()
|
|
||||||
self.stop_metadata_backup()
|
self.stop_metadata_backup()
|
||||||
self.start_metadata_backup()
|
self.start_metadata_backup()
|
||||||
def refresh_cover(event, ids):
|
|
||||||
if event == 'cover' and self.cover_cache is not None:
|
|
||||||
self.cover_cache.refresh(ids)
|
|
||||||
db.add_listener(refresh_cover)
|
|
||||||
|
|
||||||
def start_metadata_backup(self):
|
def start_metadata_backup(self):
|
||||||
self.metadata_backup = MetadataBackup(self.db)
|
self.metadata_backup = MetadataBackup(self.db)
|
||||||
@ -225,7 +210,6 @@ class BooksModel(QAbstractTableModel): # {{{
|
|||||||
|
|
||||||
def books_deleted(self):
|
def books_deleted(self):
|
||||||
self.count_changed()
|
self.count_changed()
|
||||||
self.clear_caches()
|
|
||||||
self.reset()
|
self.reset()
|
||||||
|
|
||||||
def delete_books(self, indices):
|
def delete_books(self, indices):
|
||||||
@ -254,7 +238,6 @@ class BooksModel(QAbstractTableModel): # {{{
|
|||||||
return
|
return
|
||||||
self.last_search = text
|
self.last_search = text
|
||||||
if reset:
|
if reset:
|
||||||
self.clear_caches()
|
|
||||||
self.reset()
|
self.reset()
|
||||||
if self.last_search:
|
if self.last_search:
|
||||||
# Do not issue search done for the null search. It is used to clear
|
# Do not issue search done for the null search. It is used to clear
|
||||||
@ -269,7 +252,6 @@ class BooksModel(QAbstractTableModel): # {{{
|
|||||||
label = self.column_map[col]
|
label = self.column_map[col]
|
||||||
self.db.sort(label, ascending)
|
self.db.sort(label, ascending)
|
||||||
if reset:
|
if reset:
|
||||||
self.clear_caches()
|
|
||||||
self.reset()
|
self.reset()
|
||||||
self.sorted_on = (label, order)
|
self.sorted_on = (label, order)
|
||||||
self.sort_history.insert(0, self.sorted_on)
|
self.sort_history.insert(0, self.sorted_on)
|
||||||
@ -357,26 +339,10 @@ class BooksModel(QAbstractTableModel): # {{{
|
|||||||
data[name] = val
|
data[name] = val
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def set_cache(self, idx):
|
|
||||||
l, r = 0, self.count()-1
|
|
||||||
if self.cover_cache is not None:
|
|
||||||
l = max(l, idx-self.buffer_size)
|
|
||||||
r = min(r, idx+self.buffer_size)
|
|
||||||
k = min(r-idx, idx-l)
|
|
||||||
ids = [idx]
|
|
||||||
for i in range(1, k):
|
|
||||||
ids.extend([idx-i, idx+i])
|
|
||||||
ids = ids + [i for i in range(l, r, 1) if i not in ids]
|
|
||||||
try:
|
|
||||||
ids = [self.db.id(i) for i in ids]
|
|
||||||
except IndexError:
|
|
||||||
return
|
|
||||||
self.cover_cache.set_cache(ids)
|
|
||||||
|
|
||||||
def current_changed(self, current, previous, emit_signal=True):
|
def current_changed(self, current, previous, emit_signal=True):
|
||||||
if current.isValid():
|
if current.isValid():
|
||||||
idx = current.row()
|
idx = current.row()
|
||||||
self.set_cache(idx)
|
|
||||||
data = self.get_book_display_info(idx)
|
data = self.get_book_display_info(idx)
|
||||||
if emit_signal:
|
if emit_signal:
|
||||||
self.new_bookdisplay_data.emit(data)
|
self.new_bookdisplay_data.emit(data)
|
||||||
@ -533,12 +499,6 @@ class BooksModel(QAbstractTableModel): # {{{
|
|||||||
def cover(self, row_number):
|
def cover(self, row_number):
|
||||||
data = None
|
data = None
|
||||||
try:
|
try:
|
||||||
id = self.db.id(row_number)
|
|
||||||
if self.cover_cache is not None:
|
|
||||||
img = self.cover_cache.cover(id)
|
|
||||||
if not img.isNull():
|
|
||||||
return img
|
|
||||||
if not data:
|
|
||||||
data = self.db.cover(row_number)
|
data = self.db.cover(row_number)
|
||||||
except IndexError: # Happens if database has not yet been refreshed
|
except IndexError: # Happens if database has not yet been refreshed
|
||||||
pass
|
pass
|
||||||
|
@ -583,9 +583,6 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
|
|||||||
while self.spare_servers:
|
while self.spare_servers:
|
||||||
self.spare_servers.pop().close()
|
self.spare_servers.pop().close()
|
||||||
self.device_manager.keep_going = False
|
self.device_manager.keep_going = False
|
||||||
cc = self.library_view.model().cover_cache
|
|
||||||
if cc is not None:
|
|
||||||
cc.stop()
|
|
||||||
mb = self.library_view.model().metadata_backup
|
mb = self.library_view.model().metadata_backup
|
||||||
if mb is not None:
|
if mb is not None:
|
||||||
mb.stop()
|
mb.stop()
|
||||||
|
@ -9,10 +9,8 @@ __docformat__ = 'restructuredtext en'
|
|||||||
import re, itertools, time, traceback
|
import re, itertools, time, traceback
|
||||||
from itertools import repeat
|
from itertools import repeat
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from threading import Thread, RLock
|
from threading import Thread
|
||||||
from Queue import Queue, Empty
|
from Queue import Empty
|
||||||
|
|
||||||
from PyQt4.Qt import QImage, Qt
|
|
||||||
|
|
||||||
from calibre.utils.config import tweaks
|
from calibre.utils.config import tweaks
|
||||||
from calibre.utils.date import parse_date, now, UNDEFINED_DATE
|
from calibre.utils.date import parse_date, now, UNDEFINED_DATE
|
||||||
@ -20,7 +18,7 @@ from calibre.utils.search_query_parser import SearchQueryParser
|
|||||||
from calibre.utils.pyparsing import ParseException
|
from calibre.utils.pyparsing import ParseException
|
||||||
from calibre.ebooks.metadata import title_sort
|
from calibre.ebooks.metadata import title_sort
|
||||||
from calibre.ebooks.metadata.opf2 import metadata_to_opf
|
from calibre.ebooks.metadata.opf2 import metadata_to_opf
|
||||||
from calibre import fit_image, prints
|
from calibre import prints
|
||||||
|
|
||||||
class MetadataBackup(Thread): # {{{
|
class MetadataBackup(Thread): # {{{
|
||||||
'''
|
'''
|
||||||
@ -118,113 +116,6 @@ class MetadataBackup(Thread): # {{{
|
|||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
class CoverCache(Thread): # {{{
|
|
||||||
|
|
||||||
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 = {}
|
|
||||||
self.lock = RLock()
|
|
||||||
self.allowed_ids = frozenset([])
|
|
||||||
self.null_image = QImage()
|
|
||||||
|
|
||||||
def stop(self):
|
|
||||||
self.keep_running = False
|
|
||||||
|
|
||||||
def _image_for_id(self, id_):
|
|
||||||
img = self.cover_func(id_, index_is_id=True, as_image=True)
|
|
||||||
if img is None:
|
|
||||||
img = QImage()
|
|
||||||
if not img.isNull():
|
|
||||||
scaled, nwidth, nheight = fit_image(img.width(),
|
|
||||||
img.height(), 600, 800)
|
|
||||||
if scaled:
|
|
||||||
img = img.scaled(nwidth, nheight, Qt.KeepAspectRatio,
|
|
||||||
Qt.SmoothTransformation)
|
|
||||||
|
|
||||||
return img
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
while self.keep_running:
|
|
||||||
try:
|
|
||||||
# The GUI puts the same ID into the queue many times. The code
|
|
||||||
# below emptys the queue, building a set of unique values. When
|
|
||||||
# the queue is empty, do the work
|
|
||||||
ids = set()
|
|
||||||
id_ = self.load_queue.get(True, 2)
|
|
||||||
ids.add(id_)
|
|
||||||
try:
|
|
||||||
while True:
|
|
||||||
# Give the gui some time to put values into the queue
|
|
||||||
id_ = self.load_queue.get(True, 0.5)
|
|
||||||
ids.add(id_)
|
|
||||||
except Empty:
|
|
||||||
pass
|
|
||||||
except:
|
|
||||||
# Happens during shutdown
|
|
||||||
break
|
|
||||||
except Empty:
|
|
||||||
continue
|
|
||||||
except:
|
|
||||||
#Happens during interpreter shutdown
|
|
||||||
break
|
|
||||||
if not self.keep_running:
|
|
||||||
break
|
|
||||||
for id_ in ids:
|
|
||||||
time.sleep(0.050) # Limit 20/second to not overwhelm the GUI
|
|
||||||
if not self.keep_running:
|
|
||||||
return
|
|
||||||
with self.lock:
|
|
||||||
if id_ not in self.allowed_ids:
|
|
||||||
continue
|
|
||||||
try:
|
|
||||||
img = self._image_for_id(id_)
|
|
||||||
except:
|
|
||||||
try:
|
|
||||||
traceback.print_exc()
|
|
||||||
except:
|
|
||||||
# happens during shutdown
|
|
||||||
break
|
|
||||||
continue
|
|
||||||
try:
|
|
||||||
with self.lock:
|
|
||||||
self.cache[id_] = img
|
|
||||||
except:
|
|
||||||
# Happens during interpreter shutdown
|
|
||||||
break
|
|
||||||
|
|
||||||
def set_cache(self, ids):
|
|
||||||
with self.lock:
|
|
||||||
self.allowed_ids = frozenset(ids)
|
|
||||||
already_loaded = set([])
|
|
||||||
for id in self.cache.keys():
|
|
||||||
if id in ids:
|
|
||||||
already_loaded.add(id)
|
|
||||||
else:
|
|
||||||
self.cache.pop(id)
|
|
||||||
for id_ in set(ids) - already_loaded:
|
|
||||||
self.load_queue.put(id_)
|
|
||||||
|
|
||||||
def cover(self, id_):
|
|
||||||
with self.lock:
|
|
||||||
return self.cache.get(id_, self.null_image)
|
|
||||||
|
|
||||||
def clear_cache(self):
|
|
||||||
with self.lock:
|
|
||||||
self.cache = {}
|
|
||||||
|
|
||||||
def refresh(self, ids):
|
|
||||||
with self.lock:
|
|
||||||
for id_ in ids:
|
|
||||||
cover = self.cache.pop(id_, None)
|
|
||||||
if cover is not None:
|
|
||||||
self.load_queue.put(id_)
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
### Global utility function for get_match here and in gui2/library.py
|
### Global utility function for get_match here and in gui2/library.py
|
||||||
CONTAINS_MATCH = 0
|
CONTAINS_MATCH = 0
|
||||||
EQUALS_MATCH = 1
|
EQUALS_MATCH = 1
|
||||||
|
Loading…
x
Reference in New Issue
Block a user