From df6fabfff4e96a3cd4885657463d216797b11855 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 30 Mar 2011 12:49:22 -0600 Subject: [PATCH] Make sorting on the device view faster and more robust. Fixes #742626 (Error when transferring to iTunes) --- src/calibre/gui2/library/models.py | 89 ++++++++++++++---------------- 1 file changed, 42 insertions(+), 47 deletions(-) diff --git a/src/calibre/gui2/library/models.py b/src/calibre/gui2/library/models.py index c921ea125f..e9c2621237 100644 --- a/src/calibre/gui2/library/models.py +++ b/src/calibre/gui2/library/models.py @@ -7,7 +7,6 @@ __docformat__ = 'restructuredtext en' import shutil, functools, re, os, traceback from contextlib import closing -from operator import attrgetter from PyQt4.Qt import QAbstractTableModel, Qt, pyqtSignal, QIcon, QImage, \ QModelIndex, QVariant, QDate, QColor @@ -18,7 +17,7 @@ from calibre.ebooks.metadata import fmt_sidx, authors_to_string, string_to_autho from calibre.ptempfile import PersistentTemporaryFile from calibre.utils.config import tweaks, prefs from calibre.utils.date import dt_factory, qt_to_dt, isoformat -from calibre.utils.icu import sort_key, strcmp as icu_strcmp +from calibre.utils.icu import sort_key from calibre.ebooks.metadata.meta import set_metadata as _set_metadata from calibre.utils.search_query_parser import SearchQueryParser from calibre.library.caches import _match, CONTAINS_MATCH, EQUALS_MATCH, \ @@ -984,6 +983,21 @@ class OnDeviceSearch(SearchQueryParser): # {{{ # }}} +class DeviceDBSortKeyGen(object): # {{{ + + def __init__(self, attr, keyfunc, db): + self.attr = attr + self.db = db + self.keyfunc = keyfunc + + def __call__(self, x): + try: + ans = self.keyfunc(getattr(self.db[x], self.attr)) + except: + ans = None + return ans +# }}} + class DeviceBooksModel(BooksModel): # {{{ booklist_dirtied = pyqtSignal() @@ -1089,59 +1103,40 @@ class DeviceBooksModel(BooksModel): # {{{ def sort(self, col, order, reset=True): descending = order != Qt.AscendingOrder - def strcmp(attr): - ag = attrgetter(attr) - def _strcmp(x, y): - x = ag(self.db[x]) - y = ag(self.db[y]) - if x == None: - x = '' - if y == None: - y = '' - return icu_strcmp(x.strip(), y.strip()) - return _strcmp - def datecmp(x, y): - x = self.db[x].datetime - y = self.db[y].datetime - return cmp(dt_factory(x, assume_utc=True), dt_factory(y, - assume_utc=True)) - def sizecmp(x, y): - x, y = int(self.db[x].size), int(self.db[y].size) - return cmp(x, y) - def tagscmp(x, y): - x = ','.join(sorted(getattr(self.db[x], 'device_collections', []),key=sort_key)) - y = ','.join(sorted(getattr(self.db[y], 'device_collections', []),key=sort_key)) - return cmp(x, y) - def libcmp(x, y): - x, y = self.db[x].in_library, self.db[y].in_library - return cmp(x, y) - def authorcmp(x, y): - ax = getattr(self.db[x], 'author_sort', None) - ay = getattr(self.db[y], 'author_sort', None) - if ax and ay: - x = ax - y = ay - else: - x, y = authors_to_string(self.db[x].authors), \ - authors_to_string(self.db[y].authors) - return cmp(x, y) cname = self.column_map[col] - fcmp = { - 'title': strcmp('title_sorter'), - 'authors' : authorcmp, - 'size' : sizecmp, - 'timestamp': datecmp, - 'collections': tagscmp, - 'inlibrary': libcmp, + def author_key(x): + try: + ax = self.db[x].author_sort + if not ax: + raise Exception('') + except: + try: + ax = authors_to_string(self.db[x].authors) + except: + ax = '' + return ax + + keygen = { + 'title': ('title_sorter', lambda x: sort_key(x) if x else ''), + 'authors' : author_key, + 'size' : ('size', int), + 'timestamp': ('datetime', functools.partial(dt_factory, assume_utc=True)), + 'collections': ('device_collections', lambda x:sorted(x, + key=sort_key)), + 'inlibrary': ('in_library', lambda x: x), }[cname] - self.map.sort(cmp=fcmp, reverse=descending) + keygen = keygen if callable(keygen) else DeviceDBSortKeyGen( + keygen[0], keygen[1], self.db) + self.map.sort(key=keygen, reverse=descending) if len(self.map) == len(self.db): self.sorted_map = list(self.map) else: self.sorted_map = list(range(len(self.db))) - self.sorted_map.sort(cmp=fcmp, reverse=descending) + self.sorted_map.sort(cmp=keygen, reverse=descending) self.sorted_on = (self.column_map[col], order) self.sort_history.insert(0, self.sorted_on) + if hasattr(keygen, 'db'): + keygen.db = None if reset: self.reset()