mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Bug #1920733: Slight lag when viewing composite column in quickview.
This problem is caused by Quickview filling in the entite table whenever the book changes. Unfortunately this is necessary because the table is sorted. I improved performance using two methods: - Compute the data for a column only when it is needed. This means that in general only the data in the sorted column is computed before displaying the table. - Delay a bit before redisplaying the table. This lets the main gui remain responsive at the cost of a small delay in seeing the quickview data.
This commit is contained in:
parent
33eac400fd
commit
3c781d1334
@ -12,7 +12,7 @@ from functools import partial
|
||||
from qt.core import (
|
||||
Qt, QDialog, QAbstractItemView, QTableWidgetItem, QIcon, QListWidgetItem,
|
||||
QCoreApplication, QEvent, QObject, QApplication, pyqtSignal, QByteArray, QMenu,
|
||||
QShortcut)
|
||||
QShortcut, QTimer)
|
||||
|
||||
from calibre.customize.ui import find_plugin
|
||||
from calibre.gui2 import gprefs
|
||||
@ -29,13 +29,18 @@ class TableItem(QTableWidgetItem):
|
||||
A QTableWidgetItem that sorts on a separate string and uses ICU rules
|
||||
'''
|
||||
|
||||
def __init__(self, val, sort, idx=0):
|
||||
self.sort = sort
|
||||
self.sort_idx = idx
|
||||
QTableWidgetItem.__init__(self, val)
|
||||
def __init__(self, getter=None):
|
||||
self.val = ''
|
||||
self.sort = None
|
||||
self.sort_idx = 0
|
||||
self.getter = getter
|
||||
self.resolved = False
|
||||
QTableWidgetItem.__init__(self, '')
|
||||
self.setFlags(Qt.ItemFlag.ItemIsEnabled|Qt.ItemFlag.ItemIsSelectable)
|
||||
|
||||
def __ge__(self, other):
|
||||
self.get_data()
|
||||
other.get_data()
|
||||
if self.sort is None:
|
||||
if other.sort is None:
|
||||
# None == None therefore >=
|
||||
@ -59,6 +64,8 @@ class TableItem(QTableWidgetItem):
|
||||
return 0
|
||||
|
||||
def __lt__(self, other):
|
||||
self.get_data()
|
||||
other.get_data()
|
||||
if self.sort is None:
|
||||
if other.sort is None:
|
||||
# None == None therefore not <
|
||||
@ -81,6 +88,17 @@ class TableItem(QTableWidgetItem):
|
||||
return self.sort_idx < other.sort_idx
|
||||
return 0
|
||||
|
||||
def get_data(self):
|
||||
if not self.resolved and self.getter:
|
||||
self.resolved = True
|
||||
self.val, self.sort, self.sort_idx = self.getter()
|
||||
|
||||
def data(self, role):
|
||||
self.get_data()
|
||||
if role == Qt.DisplayRole:
|
||||
return self.val
|
||||
return QTableWidgetItem.data(self, role)
|
||||
|
||||
|
||||
IN_WIDGET_ITEMS = 0
|
||||
IN_WIDGET_BOOKS = 1
|
||||
@ -228,7 +246,7 @@ class Quickview(QDialog, Ui_Quickview):
|
||||
# resizeRowsToContents can word wrap long cell contents, creating
|
||||
# double-high rows
|
||||
self.books_table.setRowCount(1)
|
||||
self.books_table.setItem(0, 0, TableItem('A', ''))
|
||||
self.books_table.setItem(0, 0, TableItem())
|
||||
self.books_table.resizeRowsToContents()
|
||||
self.books_table_row_height = self.books_table.rowHeight(0)
|
||||
self.books_table.setRowCount(0)
|
||||
@ -236,10 +254,13 @@ class Quickview(QDialog, Ui_Quickview):
|
||||
# Add the data
|
||||
self.refresh(row)
|
||||
|
||||
self.view.clicked.connect(self.slave)
|
||||
self.view.selectionModel().currentColumnChanged.connect(self.column_slave)
|
||||
self.slave_timers = [QTimer(), QTimer(), QTimer()]
|
||||
self.view.clicked.connect(partial(self.delayed_slave, func=self.slave, dex=0))
|
||||
self.view.selectionModel().currentColumnChanged.connect(
|
||||
partial(self.delayed_slave, func=self.column_slave, dex=1))
|
||||
QCoreApplication.instance().aboutToQuit.connect(self.save_state)
|
||||
self.view.model().new_bookdisplay_data.connect(self.book_was_changed)
|
||||
self.view.model().new_bookdisplay_data.connect(
|
||||
partial(self.delayed_slave, func=self.book_was_changed, dex=2))
|
||||
|
||||
self.close_button.setDefault(False)
|
||||
self.close_button_tooltip = _('The Quickview shortcut ({0}) shows/hides the Quickview panel')
|
||||
@ -285,6 +306,14 @@ class Quickview(QDialog, Ui_Quickview):
|
||||
self.close_button.setToolTip(_('Alternate shortcut: ') +
|
||||
toggle_shortcut.toString())
|
||||
|
||||
def delayed_slave(self, current, func=None, dex=None):
|
||||
self.slave_timers[dex].stop()
|
||||
t = self.slave_timers[dex] = QTimer()
|
||||
t.timeout.connect(partial(func, current))
|
||||
t.setSingleShot(True)
|
||||
t.setInterval(200)
|
||||
t.start()
|
||||
|
||||
def item_doubleclicked(self, item):
|
||||
tb = self.gui.stack.tb_widget
|
||||
tb.set_focus_to_find_box()
|
||||
@ -513,7 +542,7 @@ class Quickview(QDialog, Ui_Quickview):
|
||||
self.items.clear()
|
||||
self.books_table.setRowCount(0)
|
||||
|
||||
mi = self.db.get_metadata(book_id, index_is_id=True, get_user_categories=False)
|
||||
mi = self.db.new_api.get_proxy_metadata(book_id)
|
||||
vals = mi.get(key, None)
|
||||
if self.fm[key]['datatype'] == 'composite' and self.fm[key]['is_multiple']:
|
||||
sep = self.fm[key]['is_multiple'].get('cache_to_list', ',')
|
||||
@ -605,47 +634,12 @@ class Quickview(QDialog, Ui_Quickview):
|
||||
'which also changes the selected book.'
|
||||
) + '</p>')
|
||||
for row, b in enumerate(books):
|
||||
mi = self.db.new_api.get_proxy_metadata(b)
|
||||
for col in self.column_order:
|
||||
try:
|
||||
if col == 'title':
|
||||
a = TableItem(mi.title, mi.title_sort)
|
||||
if b == self.current_book_id:
|
||||
select_item = a
|
||||
elif col == 'authors':
|
||||
a = TableItem(' & '.join(mi.authors), mi.author_sort)
|
||||
elif col == 'series':
|
||||
series = mi.format_field('series')[1]
|
||||
if series is None:
|
||||
a = TableItem('', '', 0)
|
||||
else:
|
||||
a = TableItem(series, mi.series, mi.series_index)
|
||||
elif col == 'size':
|
||||
v = mi.get('book_size')
|
||||
if v is not None:
|
||||
a = TableItem('{:n}'.format(v), v)
|
||||
a.setTextAlignment(Qt.AlignmentFlag.AlignRight)
|
||||
else:
|
||||
a = TableItem(' ', None)
|
||||
elif self.fm[col]['datatype'] == 'series':
|
||||
v = mi.format_field(col)[1]
|
||||
a = TableItem(v, mi.get(col), mi.get(col+'_index'))
|
||||
elif self.fm[col]['datatype'] == 'datetime':
|
||||
v = mi.format_field(col)[1]
|
||||
d = mi.get(col)
|
||||
if d is None:
|
||||
d = UNDEFINED_DATE
|
||||
a = TableItem(v, timestampfromdt(d))
|
||||
elif self.fm[col]['datatype'] in ('float', 'int'):
|
||||
v = mi.format_field(col)[1]
|
||||
sort_val = mi.get(col)
|
||||
a = TableItem(v, sort_val)
|
||||
else:
|
||||
v = mi.format_field(col)[1]
|
||||
a = TableItem(v, v)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
a = TableItem(_('Something went wrong while filling in the table'), '')
|
||||
a = TableItem(partial(self.get_item_data, b, col))
|
||||
if col == 'title':
|
||||
if b == self.current_book_id:
|
||||
select_item = a
|
||||
# The data is supplied on demand when the item is displayed
|
||||
a.setData(Qt.ItemDataRole.UserRole, b)
|
||||
a.setToolTip(tt)
|
||||
self.books_table.setItem(row, self.key_to_table_widget_column(col), a)
|
||||
@ -657,6 +651,45 @@ class Quickview(QDialog, Ui_Quickview):
|
||||
self.books_table.scrollToItem(select_item, QAbstractItemView.ScrollHint.PositionAtCenter)
|
||||
self.set_search_text(sv)
|
||||
|
||||
def get_item_data(self, book_id, col):
|
||||
mi = self.db.new_api.get_proxy_metadata(book_id)
|
||||
try:
|
||||
if col == 'title':
|
||||
return (mi.title, mi.title_sort, 0)
|
||||
elif col == 'authors':
|
||||
return (' & '.join(mi.authors), mi.author_sort, 0)
|
||||
elif col == 'series':
|
||||
series = mi.format_field('series')[1]
|
||||
if series is None:
|
||||
return ('', None, 0)
|
||||
else:
|
||||
return (series, mi.series, mi.series_index)
|
||||
elif col == 'size':
|
||||
v = mi.get('book_size')
|
||||
if v is not None:
|
||||
return ('{:n}'.format(v), v, 0)
|
||||
else:
|
||||
return ('', None, 0)
|
||||
elif self.fm[col]['datatype'] == 'series':
|
||||
v = mi.format_field(col)[1]
|
||||
return (v, mi.get(col), mi.get(col+'_index'))
|
||||
elif self.fm[col]['datatype'] == 'datetime':
|
||||
v = mi.format_field(col)[1]
|
||||
d = mi.get(col)
|
||||
if d is None:
|
||||
d = UNDEFINED_DATE
|
||||
return (v, timestampfromdt(d), 0)
|
||||
elif self.fm[col]['datatype'] in ('float', 'int'):
|
||||
v = mi.format_field(col)[1]
|
||||
sort_val = mi.get(col)
|
||||
return (v, sort_val, 0)
|
||||
else:
|
||||
v = mi.format_field(col)[1]
|
||||
return (v, v, 0)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
return (_('Something went wrong while filling in the table'), '', 0)
|
||||
|
||||
# Deal with sizing the table columns. Done here because the numbers are not
|
||||
# correct until the first paint.
|
||||
def resizeEvent(self, *args):
|
||||
|
Loading…
x
Reference in New Issue
Block a user