mirror of
git://github.com/kovidgoyal/calibre.git
synced 2026-05-08 22:23:25 +02: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
1 changed files with 83 additions and 50 deletions
|
|
@ -12,7 +12,7 @@
|
|||
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 @@ def __ge__(self, other):
|
|||
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 @@ def __lt__(self, other):
|
|||
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 @@ def __init__(self, gui, row, toggle_shortcut):
|
|||
# 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 @@ def __init__(self, gui, row, toggle_shortcut):
|
|||
# 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 @@ def __init__(self, gui, row, toggle_shortcut):
|
|||
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 @@ def _refresh(self, book_id, key):
|
|||
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 @@ def fill_in_books_box(self, selected_item):
|
|||
'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 @@ def fill_in_books_box(self, selected_item):
|
|||
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…
Reference in a new issue