From f947e0df48f73596609d240f1875962d96a01a4f Mon Sep 17 00:00:00 2001 From: Charles Haley Date: Sun, 16 Oct 2022 13:24:26 +0100 Subject: [PATCH] Two performance improvements related to large libraries. See https://www.mobileread.com/forums/showthread.php?p=4265937#post4265937 for an explanation. The "exotic" part is changing _map_filtered to a property with a setter. The setter constructs a dict {book_id:row}. view.id_to_index() uses the dict instead of .index on the list. --- src/calibre/db/view.py | 28 ++++++++++++++------- src/calibre/gui2/library/alternate_views.py | 5 ++-- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/calibre/db/view.py b/src/calibre/db/view.py index c03d82dea8..1d6ad37c92 100644 --- a/src/calibre/db/view.py +++ b/src/calibre/db/view.py @@ -149,6 +149,8 @@ class View: fmt = partial(format_is_multiple, sep=sep) self._field_getters[idx] = partial(func, label, fmt=fmt) if func == self._get else func + self._real_map_filtered = tuple() + self._real_map_filtered_dict = dict() self._map = tuple(sorted(self.cache.all_book_ids())) self._map_filtered = tuple(self._map) self.full_map_is_sorted = True @@ -171,6 +173,15 @@ class View: def sanitize_sort_field_name(self, field): return sanitize_sort_field_name(self.field_metadata, field) + @property + def _map_filtered(self): + return self._real_map_filtered + + @_map_filtered.setter + def _map_filtered(self, v): + self._real_map_filtered = v + self._real_map_filtered_dict = {id_:row for row, id_ in enumerate(self._map_filtered)} + @property def field_metadata(self): return self.cache.field_metadata @@ -215,12 +226,16 @@ class View: return self._map_filtered[idx] def id_to_index(self, book_id): - return self._map_filtered.index(book_id) + try: + return self._real_map_filtered_dict[book_id] + except KeyError: + raise ValueError(f'No such book_id {book_id}') row = index_to_id def index(self, book_id, cache=False): - x = self._map if cache else self._map_filtered - return x.index(book_id) + if cache: + return self._map.index(book_id) + return self.id_to_index(book_id) def _get(self, field, idx, index_is_id=True, default_value=None, fmt=lambda x:x): id_ = idx if index_is_id else self.index_to_id(idx) @@ -452,12 +467,7 @@ class View: # The ids list can contain invalid ids (deleted etc). We want to filter # those out while keeping the valid ones. - def f(id_): - try: - return self.id_to_index(id_) - except ValueError: - return None - res = [i for i in map(f, ids) if i is not None] + res = [self._real_map_filtered_dict[id_] for id_ in ids if id_ in self._real_map_filtered_dict] return res if res else None def remove(self, book_id): diff --git a/src/calibre/gui2/library/alternate_views.py b/src/calibre/gui2/library/alternate_views.py index d6bcf162e7..52c280b2f6 100644 --- a/src/calibre/gui2/library/alternate_views.py +++ b/src/calibre/gui2/library/alternate_views.py @@ -110,8 +110,9 @@ def event_has_mods(self, event=None): def mousePressEvent(self, event): ep = event.pos() - if self.indexAt(ep) in self.selectionModel().selectedIndexes() and \ - event.button() == Qt.MouseButton.LeftButton and not self.event_has_mods(): + # for performance, check the selection only once we know we need it + if event.button() == Qt.MouseButton.LeftButton and not self.event_has_mods() \ + and self.indexAt(ep) in self.selectionModel().selectedIndexes(): self.drag_start_pos = ep if hasattr(self, 'handle_mouse_press_event'): return self.handle_mouse_press_event(event)