Cleanup code to implement search restrictions

This commit is contained in:
Kovid Goyal 2010-06-06 09:18:01 -06:00
commit 9d4224acfa
5 changed files with 74 additions and 84 deletions

View File

@ -213,7 +213,7 @@ class BooksModel(QAbstractTableModel): # {{{
self.endInsertRows()
self.count_changed()
def search(self, text, refinement, reset=True):
def search(self, text, reset=True):
try:
self.db.search(text)
except ParseException:
@ -224,9 +224,10 @@ class BooksModel(QAbstractTableModel): # {{{
self.clear_caches()
self.reset()
if self.last_search:
# Do not issue search done for the null search. It is used to clear
# the search and count records for restrictions
self.searched.emit(True)
def sort(self, col, order, reset=True):
if not self.db:
return
@ -257,7 +258,7 @@ class BooksModel(QAbstractTableModel): # {{{
self.sort(col, self.sorted_on[1], reset=reset)
def research(self, reset=True):
self.search(self.last_search, False, reset=reset)
self.search(self.last_search, reset=reset)
def columnCount(self, parent):
if parent and parent.isValid():
@ -730,6 +731,8 @@ class BooksModel(QAbstractTableModel): # {{{
def set_search_restriction(self, s):
self.db.data.set_search_restriction(s)
self.search('')
return self.rowCount(None)
# }}}
@ -874,7 +877,7 @@ class DeviceBooksModel(BooksModel): # {{{
return flags
def search(self, text, refinement, reset=True):
def search(self, text, reset=True):
if not text or not text.strip():
self.map = list(range(len(self.db)))
else:

View File

@ -437,10 +437,6 @@ class BooksView(QTableView): # {{{
self._search_done = search_done
self._model.searched.connect(self.search_done)
def connect_to_restriction_set(self, tv):
# must be synchronous (not queued)
tv.restriction_set.connect(self._model.set_search_restriction)
def connect_to_book_display(self, bd):
self._model.new_bookdisplay_data.connect(bd)

View File

@ -57,7 +57,7 @@ class SearchBox2(QComboBox):
INTERVAL = 1500 #: Time to wait before emitting search signal
MAX_COUNT = 25
search = pyqtSignal(object, object)
search = pyqtSignal(object)
def __init__(self, parent=None):
QComboBox.__init__(self, parent)
@ -97,8 +97,12 @@ class SearchBox2(QComboBox):
self.help_state = False
def clear_to_help(self):
self.search.emit('')
self._in_a_search = False
self.setEditText(self.help_text)
if self.timer is not None: # Turn off any timers that got started in setEditText
self.killTimer(self.timer)
self.timer = None
self.line_edit.home(False)
self.help_state = True
self.line_edit.setStyleSheet(
@ -111,7 +115,6 @@ class SearchBox2(QComboBox):
def clear(self):
self.clear_to_help()
self.search.emit('', False)
def search_done(self, ok):
if not unicode(self.currentText()).strip():
@ -155,9 +158,8 @@ class SearchBox2(QComboBox):
if not text or text == self.help_text:
return self.clear()
self.help_state = False
refinement = text.startswith(self.prev_search) and ':' not in text
self.prev_search = text
self.search.emit(text, refinement)
self.search.emit(text)
idx = self.findText(text, Qt.MatchFixedString)
self.block_signals(True)
@ -192,7 +194,7 @@ class SearchBox2(QComboBox):
if self.timer is not None: # Turn off any timers that got started in setEditText
self.killTimer(self.timer)
self.timer = None
self.search.emit(txt, False)
self.search.emit(txt)
self.line_edit.end(False)
self.initial_state = False

View File

@ -22,7 +22,6 @@ from calibre.gui2 import error_dialog
class TagsView(QTreeView): # {{{
refresh_required = pyqtSignal()
restriction_set = pyqtSignal(object)
tags_marked = pyqtSignal(object, object)
user_category_edit = pyqtSignal(object)
tag_list_edit = pyqtSignal(object, object)
@ -37,12 +36,11 @@ class TagsView(QTreeView): # {{{
self.setIconSize(QSize(30, 30))
self.tag_match = None
def set_database(self, db, tag_match, popularity, restriction):
def set_database(self, db, tag_match, popularity):
self.hidden_categories = config['tag_browser_hidden_categories']
self._model = TagsModel(db, parent=self,
hidden_categories=self.hidden_categories)
self.popularity = popularity
self.restriction = restriction
self.tag_match = tag_match
self.db = db
self.setModel(self._model)
@ -51,10 +49,8 @@ class TagsView(QTreeView): # {{{
self.customContextMenuRequested.connect(self.show_context_menu)
self.popularity.setChecked(config['sort_by_popularity'])
self.popularity.stateChanged.connect(self.sort_changed)
self.restriction.activated[str].connect(self.search_restriction_set)
self.refresh_required.connect(self.recount, type=Qt.QueuedConnection)
db.add_listener(self.database_changed)
self.saved_searches_changed(recount=False)
def database_changed(self, event, ids):
self.refresh_required.emit()
@ -68,16 +64,10 @@ class TagsView(QTreeView): # {{{
self.model().refresh()
# self.search_restriction_set()
def search_restriction_set(self, s):
def set_search_restriction(self, s):
self.clear()
if len(s) == 0:
self.search_restriction = ''
else:
self.search_restriction = 'search:"%s"' % unicode(s).strip()
self.model().set_search_restriction(self.search_restriction)
self.restriction_set.emit(self.search_restriction)
self.recount() # Must happen after the emission of the restriction_set signal
self.tags_marked.emit(self._model.tokens(), self.match_all)
self.model().set_search_restriction(s)
self.recount()
def mouseReleaseEvent(self, event):
# Swallow everything except leftButton so context menus work correctly
@ -187,21 +177,8 @@ class TagsView(QTreeView): # {{{
return True
def clear(self):
self.model().clear_state()
def saved_searches_changed(self, recount=True):
p = prefs['saved_searches'].keys()
p.sort()
t = self.restriction.currentText()
self.restriction.clear() # rebuild the restrictions combobox using current saved searches
self.restriction.addItem('')
for s in p:
self.restriction.addItem(s)
if t in p: # redo the current restriction, if there was one
self.restriction.setCurrentIndex(self.restriction.findText(t))
self.search_restriction_set(t)
if recount:
self.recount()
if self.model():
self.model().clear_state()
def recount(self, *args):
ci = self.currentIndex()
@ -372,7 +349,7 @@ class TagsModel(QAbstractItemModel): # {{{
if len(self.search_restriction):
data = self.db.get_categories(sort_on_count=sort, icon_map=self.category_icon_map,
ids=self.db.search(self.search_restriction, return_matches=True))
ids=self.db.search('', return_matches=True))
else:
data = self.db.get_categories(sort_on_count=sort, icon_map=self.category_icon_map)

View File

@ -160,9 +160,9 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
self.restriction_in_effect = False
self.search.initialize('main_search_history', colorize=True,
help_text=_('Search (For Advanced Search click the button to the left)'))
self.connect(self.clear_button, SIGNAL('clicked()'), self.search_clear)
self.connect(self.clear_button, SIGNAL('clicked()'), self.search.clear)
self.connect(self.clear_button, SIGNAL('clicked()'), self.saved_search.clear_to_help)
self.search_clear()
self.search.clear()
self.saved_search.initialize(saved_searches, self.search, colorize=True,
help_text=_('Saved Searches'))
@ -226,14 +226,14 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
self.connect(self.quit_action, SIGNAL('triggered(bool)'), self.quit)
self.connect(self.donate_action, SIGNAL('triggered(bool)'), self.donate)
self.connect(self.restore_action, SIGNAL('triggered()'),
self.show_windows)
self.show_windows)
self.connect(self.action_show_book_details,
SIGNAL('triggered(bool)'), self.show_book_info)
SIGNAL('triggered(bool)'), self.show_book_info)
self.connect(self.action_restart, SIGNAL('triggered()'),
self.restart)
self.connect(self.system_tray_icon,
SIGNAL('activated(QSystemTrayIcon::ActivationReason)'),
self.system_tray_icon_activated)
SIGNAL('activated(QSystemTrayIcon::ActivationReason)'),
self.system_tray_icon_activated)
self.tool_bar.contextMenuEvent = self.no_op
####################### Start spare job server ########################
@ -521,8 +521,6 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
self.search_done)),
('connect_to_book_display',
(self.status_bar.book_info.show_data,)),
('connect_to_restriction_set',
(self.tags_view,)),
]:
for view in (self.library_view, self.memory_view, self.card_a_view, self.card_b_view):
getattr(view, func)(*args)
@ -545,10 +543,9 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
self.cover_cache.start()
self.library_view.model().cover_cache = self.cover_cache
self.connect(self.edit_categories, SIGNAL('clicked()'), self.do_user_categories_edit)
self.tags_view.set_database(db, self.tag_match, self.popularity, self.search_restriction)
self.search_restriction.currentIndexChanged[str].connect(self.apply_search_restriction)
self.tags_view.set_database(db, self.tag_match, self.popularity)
self.tags_view.tags_marked.connect(self.search.search_from_tags)
for x in (self.saved_search.clear_to_help, self.mark_restriction_set):
self.tags_view.restriction_set.connect(x)
self.tags_view.tags_marked.connect(self.saved_search.clear_to_help)
self.tags_view.tag_list_edit.connect(self.do_tags_list_edit)
self.tags_view.user_category_edit.connect(self.do_user_categories_edit)
@ -561,8 +558,8 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
self.library_view.model().count_changed_signal.connect(x)
self.connect(self.search, SIGNAL('cleared()'), self.search_box_cleared)
self.connect(self.saved_search, SIGNAL('changed()'),
self.tags_view.saved_searches_changed, Qt.QueuedConnection)
self.connect(self.saved_search, SIGNAL('changed()'), self.saved_searches_changed)
self.saved_searches_changed()
if not gprefs.get('quick_start_guide_added', False):
from calibre.ebooks.metadata import MetaInformation
mi = MetaInformation(_('Calibre Quick Start Guide'), ['John Schember'])
@ -585,7 +582,6 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
self.search_restriction.setSizeAdjustPolicy(self.search_restriction.AdjustToMinimumContentsLengthWithIcon)
self.search_restriction.setMinimumContentsLength(10)
########################### Cover Flow ################################
self.cover_flow = None
if CoverFlow is not None:
@ -625,7 +621,6 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
self.sidebar.job_done, Qt.QueuedConnection)
if config['autolaunch_server']:
from calibre.library.server.main import start_threaded_server
from calibre.library.server import server_config
@ -683,7 +678,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
d = SavedSearchEditor(self, search)
d.exec_()
if d.result() == d.Accepted:
self.tags_view.saved_searches_changed(recount=True)
self.saved_searches_changed()
self.saved_search.clear_to_help()
def resizeEvent(self, ev):
@ -842,19 +837,11 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
sm.select(idx, sm.ClearAndSelect|sm.Rows)
self.library_view.setCurrentIndex(idx)
'''
Handling of the count of books in a restricted view requires that
we capture the count after the initial restriction search. To so this,
we require that the restriction_set signal be issued before the search signal,
so that when the search_done happens and the count is displayed,
we can grab the count. This works because the search box is cleared
when a restriction is set, so that first search will find all books.
Adding and deleting books creates another complexity. When added, they are
displayed regardless of whether they match the restriction. However, if they
do not, they are removed at the next search. The counts must take this
Restrictions.
Adding and deleting books creates a complexity. When added, they are
displayed regardless of whether they match a search restriction. However, if
they do not, they are removed at the next search. The counts must take this
behavior into effect.
'''
@ -862,15 +849,25 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
self.restriction_count_of_books_in_view += c - self.restriction_count_of_books_in_library
self.restriction_count_of_books_in_library = c
if self.restriction_in_effect:
self.set_number_of_books_shown(compute_count=False)
self.set_number_of_books_shown()
def mark_restriction_set(self, r):
self.restriction_in_effect = False if r is None or not r else True
def apply_search_restriction(self, r):
r = unicode(r)
if r is not None and r != '':
self.restriction_in_effect = True
restriction = "search:%s"%(r)
else:
self.restriction_in_effect = False
restriction = ''
self.restriction_count_of_books_in_view = \
self.library_view.model().set_search_restriction(restriction)
self.search.clear_to_help()
self.saved_search.clear_to_help()
self.tags_view.set_search_restriction(restriction)
self.set_number_of_books_shown()
def set_number_of_books_shown(self, compute_count):
def set_number_of_books_shown(self):
if self.current_view() == self.library_view and self.restriction_in_effect:
if compute_count:
self.restriction_count_of_books_in_view = self.current_view().row_count()
t = _("({0} of {1})").format(self.current_view().row_count(),
self.restriction_count_of_books_in_view)
self.search_count.setStyleSheet('QLabel { border-radius: 8px; background-color: yellow; }')
@ -884,18 +881,30 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
self.search_count.setText(t)
def search_box_cleared(self):
self.set_number_of_books_shown(compute_count=True)
self.tags_view.clear()
self.saved_search.clear_to_help()
def search_clear(self):
self.set_number_of_books_shown(compute_count=True)
self.search.clear()
self.set_number_of_books_shown()
def search_done(self, view, ok):
if view is self.current_view():
self.search.search_done(ok)
self.set_number_of_books_shown(compute_count=False)
self.set_number_of_books_shown()
def saved_searches_changed(self):
p = prefs['saved_searches'].keys()
p.sort()
t = unicode(self.search_restriction.currentText())
self.search_restriction.clear() # rebuild the restrictions combobox using current saved searches
self.search_restriction.addItem('')
for s in p:
self.search_restriction.addItem(s)
if t:
if t in p: # redo the current restriction, if there was one
self.search_restriction.setCurrentIndex(self.search_restriction.findText(t))
# self.tags_view.set_search_restriction(t)
else:
self.search_restriction.setCurrentIndex(0)
self.search.clear_to_help()
def sync_cf_to_listview(self, current, previous):
if self.cover_flow_sync_flag and self.cover_flow.isVisible() and \
@ -2304,14 +2313,17 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
def library_moved(self, newloc):
if newloc is None: return
db = LibraryDatabase2(newloc)
self.library_path = newloc
self.book_on_device(None, reset=True)
db.set_book_on_device_func(self.book_on_device)
self.library_view.set_database(db)
self.tags_view.set_database(db, self.tag_match, self.popularity)
self.library_view.model().set_book_on_device_func(self.book_on_device)
self.status_bar.clearMessage()
self.search.clear_to_help()
self.status_bar.reset_info()
self.library_view.model().count_changed()
prefs['library_path'] = self.library_path
############################################################################
@ -2358,7 +2370,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
self.search_restriction.setEnabled(False)
for action in list(self.delete_menu.actions())[1:]:
action.setEnabled(False)
self.set_number_of_books_shown(compute_count=False)
self.set_number_of_books_shown()
def device_job_exception(self, job):