From 7059a322a09a2bca655c47bc257f30ba61925593 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 4 Mar 2024 21:31:07 +0530 Subject: [PATCH] Rather than recreating QTimer objects on every update use a single one per context Also change the timeout to 100ms which is the commonly accepted threshold for human visual response. And stop the book details panel debounce timer during shutdown. The debounce timers in the popups are independent and use only data local to the popup object so probably dont need to be shutdown. --- src/calibre/gui2/__init__.py | 1 + src/calibre/gui2/dialogs/book_info.py | 19 ++++++++-------- src/calibre/gui2/library/views.py | 32 ++++++++++++++++----------- src/calibre/gui2/ui.py | 2 ++ 4 files changed, 31 insertions(+), 23 deletions(-) diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index 4ef2954cec..423791751f 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -47,6 +47,7 @@ from polyglot.builtins import iteritems, string_or_bytes del pqc, geometry_for_restore_as_dict NO_URL_FORMATTING = QUrl.UrlFormattingOption.None_ +BOOK_DETAILS_DISPLAY_DEBOUNCE_DELAY = 100 # 100 ms is threshold for human visual response class IconResourceManager: diff --git a/src/calibre/gui2/dialogs/book_info.py b/src/calibre/gui2/dialogs/book_info.py index 221543d289..e35fb71646 100644 --- a/src/calibre/gui2/dialogs/book_info.py +++ b/src/calibre/gui2/dialogs/book_info.py @@ -4,7 +4,6 @@ import textwrap from enum import IntEnum -from functools import partial from qt.core import ( QAction, QApplication, QBrush, QCheckBox, QDialog, QDialogButtonBox, QGridLayout, @@ -16,6 +15,7 @@ from qt.core import ( from calibre import fit_image from calibre.db.constants import RESOURCE_URL_SCHEME from calibre.gui2 import NO_URL_FORMATTING, gprefs +from calibre.gui2 import BOOK_DETAILS_DISPLAY_DEBOUNCE_DELAY from calibre.gui2.book_details import ( create_open_cover_with_menu, resolved_css, details_context_menu_event, render_html, set_html, ) @@ -24,8 +24,6 @@ from calibre.gui2.widgets import CoverView from calibre.gui2.widgets2 import Dialog, HTMLDisplay from calibre.startup import connect_lambda -BOOK_DETAILS_DISPLAY_DELAY = 250 # 250ms is arbitrary - class Cover(CoverView): @@ -225,7 +223,10 @@ class BookInfo(QDialog): self.path_to_book = None self.current_row = None self.slave_connected = False - self.slave_debounce_timer = QTimer() + self.slave_debounce_timer = t = QTimer(self) + t.setInterval(BOOK_DETAILS_DISPLAY_DEBOUNCE_DELAY) + t.setSingleShot(True) + t.timeout.connect(self._debounce_refresh) if library_path is not None: self.view = None db = get_gui().library_broker.get_library(library_path) @@ -349,13 +350,11 @@ class BookInfo(QDialog): QTimer.singleShot(1, self.resize_cover) def slave(self, mi): - self.slave_debounce_timer.stop() - t = self.book_display_info_timer = QTimer() - t.setSingleShot(True) - t.timeout.connect(partial(self._timed_slave, mi)) - t.start(BOOK_DETAILS_DISPLAY_DELAY) + self._mi_for_debounce = mi + self.slave_debounce_timer.start() # start() will automatically reset the timer if it was already running - def _timed_slave(self, mi): + def _debounce_refresh(self): + mi, self._mi_for_debounce = self._mi_for_debounce, None self.refresh(mi.row_number, mi) def move(self, delta=1): diff --git a/src/calibre/gui2/library/views.py b/src/calibre/gui2/library/views.py index 881d7655c8..99eb8f8184 100644 --- a/src/calibre/gui2/library/views.py +++ b/src/calibre/gui2/library/views.py @@ -18,7 +18,9 @@ from qt.core import ( from calibre import force_unicode from calibre.constants import filesystem_encoding, islinux -from calibre.gui2 import FunctionDispatcher, error_dialog, gprefs +from calibre.gui2 import ( + BOOK_DETAILS_DISPLAY_DEBOUNCE_DELAY, FunctionDispatcher, error_dialog, gprefs, +) from calibre.gui2.dialogs.enum_values_edit import EnumValuesEdit from calibre.gui2.gestures import GestureManager from calibre.gui2.library import DEFAULT_SORT @@ -27,9 +29,9 @@ from calibre.gui2.library.alternate_views import ( ) from calibre.gui2.library.delegates import ( CcBoolDelegate, CcCommentsDelegate, CcDateDelegate, CcEnumDelegate, - CcLongTextDelegate, CcMarkdownDelegate, CcNumberDelegate, CcSeriesDelegate, CcTemplateDelegate, - CcTextDelegate, CompleteDelegate, DateDelegate, LanguagesDelegate, PubDateDelegate, - RatingDelegate, SeriesDelegate, TextDelegate, + CcLongTextDelegate, CcMarkdownDelegate, CcNumberDelegate, CcSeriesDelegate, + CcTemplateDelegate, CcTextDelegate, CompleteDelegate, DateDelegate, + LanguagesDelegate, PubDateDelegate, RatingDelegate, SeriesDelegate, TextDelegate, ) from calibre.gui2.library.models import BooksModel, DeviceBooksModel from calibre.gui2.pin_columns import PinTableView @@ -1615,16 +1617,20 @@ class BooksView(QTableView): # {{{ self._model.search_done.connect(self.alternate_views.restore_current_book_state) def connect_to_book_display(self, bd): - self.connect_to_book_display_timer = QTimer() - self._model.new_bookdisplay_data.connect(partial(self._timed_connect_to_book_display, bd)) - - def _timed_connect_to_book_display(self, bd, data): - self.connect_to_book_display_timer.stop() - t = self.connect_to_book_display_timer = QTimer() + self.book_display_callback = bd + self.connect_to_book_display_timer = t = QTimer(self) t.setSingleShot(True) - t.timeout.connect(partial(bd, data)) - from calibre.gui2.dialogs.book_info import BOOK_DETAILS_DISPLAY_DELAY - t.start(BOOK_DETAILS_DISPLAY_DELAY) + t.timeout.connect(self._debounce_book_display) + t.setInterval(BOOK_DETAILS_DISPLAY_DEBOUNCE_DELAY) + self._model.new_bookdisplay_data.connect(self._timed_connect_to_book_display) + + def _timed_connect_to_book_display(self, data): + self._book_display_data = data + self.connect_to_book_display_timer.start() + + def _debounce_book_display(self): + data, self._book_display_data = self._book_display_data, None + self.book_display_callback(data) def search_done(self, ok): self._search_done(self, ok) diff --git a/src/calibre/gui2/ui.py b/src/calibre/gui2/ui.py index 12e1291f16..e1d438f9c0 100644 --- a/src/calibre/gui2/ui.py +++ b/src/calibre/gui2/ui.py @@ -1191,6 +1191,8 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{ def shutdown(self, write_settings=True): self.shutting_down = True + if hasattr(self.library_view, 'connect_to_book_display_timer'): + self.library_view.connect_to_book_display_timer.stop() self.shutdown_started.emit() self.show_shutdown_message() self.server_change_notification_timer.stop()