Implement search

This commit is contained in:
BasioMeusPuga
2019-01-09 05:58:47 +05:30
parent 026fff3d7a
commit 930a97a8fa
5 changed files with 148 additions and 56 deletions

1
TODO
View File

@@ -71,6 +71,7 @@ TODO
Disable buttons for annotations, search in images
Adjust key navigation according to viewport dimensions
Search document using QTextCursor
Redo context menu order
Filetypes:
✓ pdf support
Parse TOC

View File

@@ -398,10 +398,6 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
if not file_paths:
return
def finishing_touches():
self.profile_functions.format_contentView()
self.start_culling_timer()
print('Attempting to open: ' + ', '.join(file_paths))
contents = sorter.BookSorter(
@@ -434,11 +430,9 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
if self.settings['last_open_tab'] == this_path:
self.tabWidget.setCurrentIndex(i)
self.settings['last_open_tab'] = None
finishing_touches()
return
self.tabWidget.setCurrentIndex(self.tabWidget.count() - 1)
finishing_touches()
def start_culling_timer(self):
if self.settings['perform_culling']:
@@ -457,7 +451,7 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
# The hackiness of this hack is just...
default_size = 170 # This is size of the QIcon (160 by default) +
# minimum margin is needed between thumbnails
# minimum margin needed between thumbnails
# for n icons, the n + 1th icon will appear at > n +1.11875
# First, calculate the number of images per row
@@ -759,32 +753,6 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
required_content = current_tab.metadata['content'][chapter_number][1]
current_tab.contentView.loadImage(required_content)
def search_book(self, search_text):
if not (self.tabWidget.currentIndex() != 0
and not self.tabWidget.currentWidget().are_we_doing_images_only):
return
self.tabWidget.currentWidget().sideDock.setVisible(True)
self.tabWidget.currentWidget().sideDockTabWidget.setCurrentIndex(2)
contentView = self.tabWidget.currentWidget().contentView
text_cursor = contentView.textCursor()
something_found = True
if search_text:
text_cursor.setPosition(0, QtGui.QTextCursor.MoveAnchor)
contentView.setTextCursor(text_cursor)
contentView.verticalScrollBar().setValue(contentView.verticalScrollBar().maximum())
something_found = contentView.find(search_text)
else:
text_cursor.clearSelection()
contentView.setTextCursor(text_cursor)
# if not something_found:
# self.bookToolBar.searchBar.setStyleSheet("QLineEdit {color: red;}")
# else:
# self.bookToolBar.searchBar.setStyleSheet(self.lineEditStyleSheet)
def generate_library_context_menu(self, position):
index = self.sender().indexAt(position)
if not index.isValid():

View File

@@ -277,8 +277,7 @@ class ViewProfileModification:
self.format_contentView()
def format_contentView(self):
current_tab = self.tabWidget.widget(
self.tabWidget.currentIndex())
current_tab = self.tabWidget.currentWidget()
try:
current_metadata = current_tab.metadata

View File

@@ -171,3 +171,65 @@ class BackGroundCacheRefill(QtCore.QThread):
self.image_cache.append((next_page, refill_pixmap))
except (IndexError, TypeError):
self.image_cache.append(None)
class BackGroundTextSearch(QtCore.QThread):
def __init__(self):
super(BackGroundTextSearch, self).__init__(None)
self.search_content = None
self.search_text = None
self.case_sensitive = False
self.match_words = False
self.search_results = []
def set_search_options(
self, search_content, search_text,
case_sensitive, match_words):
self.search_content = search_content
self.search_text = search_text
self.case_sensitive = case_sensitive
self.match_words = match_words
def run(self):
if not self.search_text or len(self.search_text) < 3:
return
self.search_results = {}
# Create a new QTextDocument of each chapter and iterate
# through it looking for hits
for i in self.search_content:
chapter = i[0]
chapterDocument = QtGui.QTextDocument()
chapterDocument.setHtml(i[1])
findFlags = QtGui.QTextDocument.FindFlags(0)
if self.case_sensitive:
findFlags = findFlags | QtGui.QTextDocument.FindCaseSensitively
if self.match_words:
findFlags = findFlags | QtGui.QTextDocument.FindWholeWords
findResultCursor = chapterDocument.find(self.search_text, 0, findFlags)
while not findResultCursor.isNull():
result_position = findResultCursor.position()
surroundingTextCursor = QtGui.QTextCursor(chapterDocument)
surroundingTextCursor.setPosition(
result_position, QtGui.QTextCursor.MoveAnchor)
surroundingTextCursor.movePosition(
QtGui.QTextCursor.WordLeft, QtGui.QTextCursor.MoveAnchor, 2)
surroundingTextCursor.movePosition(
QtGui.QTextCursor.NextWord, QtGui.QTextCursor.KeepAnchor, 5) # 2n + 1
surrounding_text = surroundingTextCursor.selection().toPlainText()
surrounding_text = surrounding_text.replace('\n', ' ')
try:
self.search_results[chapter].append(
(result_position, surrounding_text))
except KeyError:
self.search_results[chapter] = [(result_position, surrounding_text)]
new_position = result_position + len(self.search_text)
findResultCursor = chapterDocument.find(
self.search_text, new_position, findFlags)

View File

@@ -25,6 +25,7 @@ from PyQt5 import QtWidgets, QtGui, QtCore
from lector.models import BookmarkProxyModel
from lector.sorter import resize_image
from lector.threaded import BackGroundTextSearch
from lector.contentwidgets import PliantQGraphicsView, PliantQTextBrowser
@@ -156,6 +157,7 @@ class Tab(QtWidgets.QWidget):
# Search view and model
self.searchLineEdit = QtWidgets.QLineEdit(self.sideDockTabWidget)
self.searchLineEdit.setFocusPolicy(QtCore.Qt.StrongFocus)
self.searchLineEdit.setClearButtonEnabled(True)
search_string = self._translate('Tab', 'Search')
self.searchLineEdit.setPlaceholderText(search_string)
@@ -190,13 +192,14 @@ class Tab(QtWidgets.QWidget):
self.searchOptionsLayout.addWidget(self.caseSensitiveSearchButton)
self.searchOptionsLayout.addWidget(self.matchWholeWordButton)
self.searchResultsListView = QtWidgets.QListView(self.sideDockTabWidget)
self.searchResultsListView.setEditTriggers(QtWidgets.QListView.NoEditTriggers)
self.searchResultsListView.doubleClicked.connect(self.go_to_search_result)
self.searchResultsTreeView = QtWidgets.QTreeView(self.sideDockTabWidget)
self.searchResultsTreeView.setHeaderHidden(True)
self.searchResultsTreeView.setEditTriggers(QtWidgets.QTreeView.NoEditTriggers)
self.searchResultsTreeView.clicked.connect(self.navigate_to_search_result)
self.searchTabLayout = QtWidgets.QVBoxLayout(self.sideDockTabWidget)
self.searchTabLayout.addLayout(self.searchOptionsLayout)
self.searchTabLayout.addWidget(self.searchResultsListView)
self.searchTabLayout.addWidget(self.searchResultsTreeView)
self.searchTabLayout.setContentsMargins(0, 0, 0, 0)
self.searchTabWidget = QtWidgets.QWidget(self.sideDockTabWidget)
self.searchTabWidget.setLayout(self.searchTabLayout)
@@ -227,6 +230,23 @@ class Tab(QtWidgets.QWidget):
self.annotationNoteDock.setWindowOpacity(.95)
self.sideDock.hide()
# Create search references
if not self.are_we_doing_images_only:
self.searchResultsModel = None
self.searchThread = BackGroundTextSearch()
self.searchThread.finished.connect(self.generate_search_result_model)
self.searchTimer = QtCore.QTimer()
self.searchTimer.setSingleShot(True)
self.searchTimer.timeout.connect(self.set_search_options)
self.searchLineEdit.textChanged.connect(lambda: self.searchTimer.start(500))
self.searchBookButton.clicked.connect(lambda: self.searchTimer.start(100))
self.caseSensitiveSearchButton.clicked.connect(lambda: self.searchTimer.start(100))
self.matchWholeWordButton.clicked.connect(lambda: self.searchTimer.start(100))
# Create tab in the central tab widget
title = self.metadata['title']
if self.main_window.settings['attenuate_titles'] and len(title) > 30:
title = title[:30] + '...'
@@ -259,6 +279,7 @@ class Tab(QtWidgets.QWidget):
if tab_required == 2:
self.sideDock.activateWindow()
self.searchLineEdit.setFocus()
self.searchLineEdit.selectAll()
self.sideDockTabWidget.setCurrentIndex(tab_required)
@@ -278,7 +299,7 @@ class Tab(QtWidgets.QWidget):
except IndexError: # The file has been deleted
pass
def set_cursor_position(self, cursor_position=None):
def set_cursor_position(self, cursor_position=None, select_chars=0):
try:
required_position = self.metadata['position']['cursor_position']
except KeyError:
@@ -296,7 +317,13 @@ class Tab(QtWidgets.QWidget):
# textCursor() RETURNS a copy of the textcursor
cursor = self.contentView.textCursor()
cursor.setPosition(
required_position, QtGui.QTextCursor.MoveAnchor)
required_position - select_chars,
QtGui.QTextCursor.MoveAnchor)
if select_chars > 0: # Select search results
cursor.movePosition(
QtGui.QTextCursor.NextCharacter,
QtGui.QTextCursor.KeepAnchor,
select_chars)
self.contentView.setTextCursor(cursor)
self.contentView.ensureCursorVisible()
@@ -624,19 +651,6 @@ class Tab(QtWidgets.QWidget):
self.bookmarkProxyModel.sort(0)
self.bookmarkTreeView.setModel(self.bookmarkProxyModel)
def update_bookmark_proxy_model(self):
pass
# TODO
# This isn't being called currently
# See if there's any rationale for keeping it / removing it
# self.bookmarkProxyModel.invalidateFilter()
# self.bookmarkProxyModel.setFilterParams(
# self.main_window.bookToolBar.searchBar.text())
# self.bookmarkProxyModel.setFilterFixedString(
# self.main_window.bookToolBar.searchBar.text())
def generate_bookmark_context_menu(self, position):
index = self.bookmarkTreeView.indexAt(position)
if not index.isValid():
@@ -673,8 +687,56 @@ class Tab(QtWidgets.QWidget):
if child_rows == 1:
self.bookmarkModel.removeRow(parent_index.row())
def go_to_search_result(self, event):
print(event)
def set_search_options(self):
search_content = (
self.metadata['content'][self.main_window.bookToolBar.tocBox.currentIndex()],)
if self.searchBookButton.isChecked():
search_content = self.metadata['content']
self.searchThread.set_search_options(
search_content,
self.searchLineEdit.text(),
self.caseSensitiveSearchButton.isChecked(),
self.matchWholeWordButton.isChecked())
self.searchThread.start()
def generate_search_result_model(self):
self.searchResultsModel = QtGui.QStandardItemModel()
search_results = self.searchThread.search_results
for i in search_results:
parentItem = QtGui.QStandardItem()
parentItem.setText(i)
parentItem.setData(True, QtCore.Qt.UserRole)
chapter_index = self.main_window.bookToolBar.tocBox.findText(
i, QtCore.Qt.MatchExactly)
for j in search_results[i]:
childItem = QtGui.QStandardItem()
childItem.setText(j[1])
childItem.setData(False, QtCore.Qt.UserRole) # Is parent?
childItem.setData(chapter_index, QtCore.Qt.UserRole + 1) # Chapter index
childItem.setData(j[0], QtCore.Qt.UserRole + 2) # Cursor Position
parentItem.appendRow(childItem)
self.searchResultsModel.appendRow(parentItem)
self.searchResultsTreeView.setModel(self.searchResultsModel)
self.searchResultsTreeView.expandToDepth(1)
def navigate_to_search_result(self, index):
if not index.isValid():
return
is_parent = self.searchResultsModel.data(index, QtCore.Qt.UserRole)
if is_parent:
return
chapter_index = self.searchResultsModel.data(index, QtCore.Qt.UserRole + 1)
cursor_position = self.searchResultsModel.data(index, QtCore.Qt.UserRole + 2)
self.main_window.bookToolBar.tocBox.setCurrentIndex(chapter_index)
if not self.are_we_doing_images_only:
self.set_cursor_position(
cursor_position, len(self.searchLineEdit.text()))
def hide_mouse(self):
self.contentView.viewport().setCursor(QtCore.Qt.BlankCursor)