Sync the main and grid views

This commit is contained in:
Kovid Goyal 2013-08-01 21:27:53 +05:30
parent b95b0942da
commit 7717fe6d05
2 changed files with 71 additions and 5 deletions

View File

@ -6,16 +6,28 @@ from __future__ import (unicode_literals, division, absolute_import,
__license__ = 'GPL v3' __license__ = 'GPL v3'
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>' __copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
import itertools, operator
from time import time from time import time
from collections import OrderedDict from collections import OrderedDict
from threading import Lock, Event, Thread from threading import Lock, Event, Thread
from Queue import Queue from Queue import Queue
from functools import wraps
from PyQt4.Qt import ( from PyQt4.Qt import (
QListView, QSize, QStyledItemDelegate, QModelIndex, Qt, QImage, pyqtSignal, QPalette, QColor) QListView, QSize, QStyledItemDelegate, QModelIndex, Qt, QImage, pyqtSignal,
QPalette, QColor, QItemSelection)
from calibre import fit_image from calibre import fit_image
def sync(func):
@wraps(func)
def ans(self, *args, **kwargs):
if self.break_link or self.current_view is self.main_view:
return
with self:
return func(self, *args, **kwargs)
return ans
class AlternateViews(object): class AlternateViews(object):
def __init__(self, main_view): def __init__(self, main_view):
@ -23,6 +35,8 @@ class AlternateViews(object):
self.stack_positions = {None:0} self.stack_positions = {None:0}
self.current_view = self.main_view = main_view self.current_view = self.main_view = main_view
self.stack = None self.stack = None
self.break_link = False
self.main_connected = False
def set_stack(self, stack): def set_stack(self, stack):
self.stack = stack self.stack = stack
@ -34,6 +48,8 @@ class AlternateViews(object):
self.stack.addWidget(view) self.stack.addWidget(view)
self.stack.setCurrentIndex(0) self.stack.setCurrentIndex(0)
view.setModel(self.main_view._model) view.setModel(self.main_view._model)
view.selectionModel().currentChanged.connect(self.slave_current_changed)
view.selectionModel().selectionChanged.connect(self.slave_selection_changed)
def show_view(self, key=None): def show_view(self, key=None):
view = self.views[key] view = self.views[key]
@ -42,13 +58,45 @@ class AlternateViews(object):
self.stack.setCurrentIndex(self.stack_positions[key]) self.stack.setCurrentIndex(self.stack_positions[key])
self.current_view = view self.current_view = view
if view is not self.main_view: if view is not self.main_view:
self.main_current_changed(self.main_view.currentIndex())
self.main_selection_changed()
view.shown() view.shown()
if not self.main_connected:
self.main_connected = True
self.main_view.selectionModel().currentChanged.connect(self.main_current_changed)
self.main_view.selectionModel().selectionChanged.connect(self.main_selection_changed)
view.setFocus(Qt.OtherFocusReason)
def set_database(self, db, stage=0): def set_database(self, db, stage=0):
for view in self.views.itervalues(): for view in self.views.itervalues():
if view is not self.main_view: if view is not self.main_view:
view.set_database(db, stage=stage) view.set_database(db, stage=stage)
def __enter__(self):
self.break_link = True
def __exit__(self, *args):
self.break_link = False
@sync
def slave_current_changed(self, current, *args):
self.main_view.set_current_row(current.row(), for_sync=True)
@sync
def slave_selection_changed(self, *args):
rows = {r.row() for r in self.current_view.selectionModel().selectedIndexes()}
self.main_view.select_rows(rows, using_ids=False, change_current=False, scroll=False)
@sync
def main_current_changed(self, current, *args):
self.current_view.set_current_row(current.row())
@sync
def main_selection_changed(self, *args):
rows = {r.row() for r in self.main_view.selectionModel().selectedIndexes()}
self.current_view.select_rows(rows)
class CoverCache(dict): class CoverCache(dict):
def __init__(self, limit=200): def __init__(self, limit=200):
@ -232,5 +280,19 @@ class GridView(QListView):
else: else:
self.delegate.cover_cache.clear() self.delegate.cover_cache.clear()
def select_rows(self, rows):
sel = QItemSelection()
sm = self.selectionModel()
m = self.model()
# Create a range based selector for each set of contiguous rows
# as supplying selectors for each individual row causes very poor
# performance if a large number of rows has to be selected.
for k, g in itertools.groupby(enumerate(rows), lambda (i,x):i-x):
group = list(map(operator.itemgetter(1), g))
sel.merge(QItemSelection(m.index(min(group), 0), m.index(max(group), 0)), sm.Select)
sm.select(sel, sm.ClearAndSelect)
def set_current_row(self, row):
sm = self.selectionModel()
sm.setCurrentIndex(self.model().index(row, 0), sm.NoUpdate)

View File

@ -863,7 +863,7 @@ class BooksView(QTableView): # {{{
self.scrollTo(self.model().index(row, i), self.PositionAtCenter) self.scrollTo(self.model().index(row, i), self.PositionAtCenter)
break break
def set_current_row(self, row=0, select=True): def set_current_row(self, row=0, select=True, for_sync=False):
if row > -1 and row < self.model().rowCount(QModelIndex()): if row > -1 and row < self.model().rowCount(QModelIndex()):
h = self.horizontalHeader() h = self.horizontalHeader()
logical_indices = list(range(h.count())) logical_indices = list(range(h.count()))
@ -876,10 +876,14 @@ class BooksView(QTableView): # {{{
pairs.sort(cmp=lambda x,y:cmp(x[1], y[1])) pairs.sort(cmp=lambda x,y:cmp(x[1], y[1]))
i = pairs[0][0] i = pairs[0][0]
index = self.model().index(row, i) index = self.model().index(row, i)
self.setCurrentIndex(index) if for_sync:
if select:
sm = self.selectionModel() sm = self.selectionModel()
sm.select(index, sm.ClearAndSelect|sm.Rows) sm.setCurrentIndex(index, sm.NoUpdate)
else:
self.setCurrentIndex(index)
if select:
sm = self.selectionModel()
sm.select(index, sm.ClearAndSelect|sm.Rows)
def keyPressEvent(self, ev): def keyPressEvent(self, ev):
val = self.horizontalScrollBar().value() val = self.horizontalScrollBar().value()