Overhaul TOC generation and navigation

This commit is contained in:
BasioMeusPuga
2019-01-26 19:03:30 +05:30
parent 66746b4eaa
commit 739b84e9f4
11 changed files with 335 additions and 200 deletions

2
TODO
View File

@@ -94,6 +94,8 @@ TODO
Colors aren't loaded properly for annotation previews Colors aren't loaded properly for annotation previews
Last line in QTextBrowser should never be cut off Last line in QTextBrowser should never be cut off
Something is wrong with image alignment Something is wrong with image alignment
Scrolling the toc Combobox does nothing
fb2 images need a newline preceding them
Secondary: Secondary:
Graphical themes Graphical themes

View File

@@ -637,14 +637,15 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
self.current_tab = self.tabWidget.currentIndex() self.current_tab = self.tabWidget.currentIndex()
# Hide bookmark and annotation widgets # Hide all side docks whenever a tab is switched
for i in range(1, self.tabWidget.count()): for i in range(1, self.tabWidget.count()):
self.tabWidget.widget(i).sideDock.setVisible(False) self.tabWidget.widget(i).sideDock.setVisible(False)
# If library
if self.tabWidget.currentIndex() == 0: if self.tabWidget.currentIndex() == 0:
self.resizeEvent() self.resizeEvent()
self.start_culling_timer() self.start_culling_timer()
if self.settings['show_bars']: if self.settings['show_bars']:
self.bookToolBar.hide() self.bookToolBar.hide()
self.libraryToolBar.show() self.libraryToolBar.show()
@@ -660,18 +661,24 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
self.statusBar.setVisible(True) self.statusBar.setVisible(True)
else: else:
if self.settings['show_bars']: if self.settings['show_bars']:
self.bookToolBar.show() self.bookToolBar.show()
self.libraryToolBar.hide() self.libraryToolBar.hide()
current_tab = self.tabWidget.currentWidget() current_tab = self.tabWidget.currentWidget()
current_metadata = current_tab.metadata self.bookToolBar.tocBox.setModel(current_tab.tocModel)
self.bookToolBar.tocTreeView.expandAll()
current_tab.set_tocBox_index(None, None)
# Needed to set the contentView widget background
# on first run. Subsequent runs might be redundant,
# but it doesn't seem to visibly affect performance
self.profile_functions.format_contentView()
self.statusBar.setVisible(False)
if self.bookToolBar.fontButton.isChecked(): if self.bookToolBar.fontButton.isChecked():
self.bookToolBar.customize_view_on() self.bookToolBar.customize_view_on()
else:
# Disable irrelevant buttons in image view mode
if current_tab.are_we_doing_images_only: if current_tab.are_we_doing_images_only:
self.bookToolBar.searchButton.setVisible(False) self.bookToolBar.searchButton.setVisible(False)
self.bookToolBar.annotationButton.setVisible(False) self.bookToolBar.annotationButton.setVisible(False)
@@ -683,23 +690,6 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
self.bookToolBar.bookSeparator2.setVisible(True) self.bookToolBar.bookSeparator2.setVisible(True)
self.bookToolBar.bookSeparator3.setVisible(True) self.bookToolBar.bookSeparator3.setVisible(True)
current_position = current_metadata['position']
current_toc = [i[0] for i in current_metadata['content']]
self.bookToolBar.tocBox.blockSignals(True)
self.bookToolBar.tocBox.clear()
self.bookToolBar.tocBox.addItems(current_toc)
if current_position:
self.bookToolBar.tocBox.setCurrentIndex(
current_position['current_chapter'] - 1)
if not current_metadata['images_only']:
current_tab.hiddenButton.animateClick(25)
self.bookToolBar.tocBox.blockSignals(False)
self.profile_functions.format_contentView()
self.statusBar.setVisible(False)
def tab_close(self, tab_index=None): def tab_close(self, tab_index=None):
if not tab_index: if not tab_index:
tab_index = self.tabWidget.currentIndex() tab_index = self.tabWidget.currentIndex()
@@ -726,18 +716,15 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
self.tabWidget.setMovable(True) self.tabWidget.setMovable(True)
def set_toc_position(self, event=None): def set_toc_position(self, event=None):
currentIndex = self.bookToolBar.tocTreeView.currentIndex()
required_position = currentIndex.data(QtCore.Qt.UserRole)
if not required_position:
return # Initial startup might return a None
# The set_content method is universal
# It's going to do position tracking
current_tab = self.tabWidget.currentWidget() current_tab = self.tabWidget.currentWidget()
current_tab.set_content(required_position)
current_tab.metadata[
'position']['current_chapter'] = event + 1
current_tab.metadata[
'position']['is_read'] = False
# Go on to change the value of the Table of Contents box
current_tab.change_chapter_tocBox()
current_tab.contentView.record_position()
self.profile_functions.format_contentView()
def set_fullscreen(self): def set_fullscreen(self):
self.tabWidget.currentWidget().go_fullscreen() self.tabWidget.currentWidget().go_fullscreen()
@@ -808,12 +795,11 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
# current state of each of the toolbar buttons # current state of each of the toolbar buttons
self.settings['double_page_mode'] = self.bookToolBar.doublePageButton.isChecked() self.settings['double_page_mode'] = self.bookToolBar.doublePageButton.isChecked()
self.settings['manga_mode'] = self.bookToolBar.mangaModeButton.isChecked() self.settings['manga_mode'] = self.bookToolBar.mangaModeButton.isChecked()
chapter_number = self.bookToolBar.tocBox.currentIndex()
# Switch page to whatever index is selected in the tocBox # Switch page to whatever index is selected in the tocBox
current_tab = self.tabWidget.currentWidget() current_tab = self.tabWidget.currentWidget()
required_content = current_tab.metadata['content'][chapter_number][1] chapter_number = current_tab.metadata['position']['current_chapter']
current_tab.contentView.loadImage(required_content) current_tab.set_content(chapter_number, False)
def generate_library_context_menu(self, position): def generate_library_context_menu(self, position):
index = self.sender().indexAt(position) index = self.sender().indexAt(position)

View File

@@ -73,12 +73,12 @@ class PliantQGraphicsView(QtWidgets.QGraphicsView):
self.generate_graphicsview_context_menu) self.generate_graphicsview_context_menu)
def loadImage(self, current_page): def loadImage(self, current_page):
all_pages = [i[1] for i in self.parent.metadata['content']] all_pages = self.parent.metadata['content']
current_page_index = all_pages.index(current_page) current_page_index = all_pages.index(current_page)
double_page_mode = False double_page_mode = False
if (self.main_window.settings['double_page_mode'] if (self.main_window.settings['double_page_mode']
and (current_page_index != 0 and current_page_index != len(all_pages) - 1)): and (current_page_index not in (0, len(all_pages) - 1))):
double_page_mode = True double_page_mode = True
def load_page(current_page): def load_page(current_page):
@@ -492,7 +492,7 @@ class PliantQTextBrowser(QtWidgets.QTextBrowser):
selected_index = self.parent.annotationListView.currentIndex() selected_index = self.parent.annotationListView.currentIndex()
self.current_annotation = self.parent.annotationModel.data( self.current_annotation = self.parent.annotationModel.data(
selected_index, QtCore.Qt.UserRole) selected_index, QtCore.Qt.UserRole)
logger.info('Selected annotation: ' + + self.current_annotation['name']) logger.info('Selected annotation: ' + self.current_annotation['name'])
def mouseReleaseEvent(self, event): def mouseReleaseEvent(self, event):
# This takes care of annotation placement # This takes care of annotation placement
@@ -716,27 +716,24 @@ class PliantWidgetsCommonFunctions:
self.change_chapter(-1) self.change_chapter(-1)
def change_chapter(self, direction, was_button_pressed=None): def change_chapter(self, direction, was_button_pressed=None):
current_toc_index = self.main_window.bookToolBar.tocBox.currentIndex() current_tab = self.pw.parent
max_toc_index = self.main_window.bookToolBar.tocBox.count() - 1 current_position = current_tab.metadata['position']['current_chapter']
if (current_toc_index < max_toc_index and direction == 1) or (
current_toc_index > 0 and direction == -1):
# Special cases for double page view # Special cases for double page view
# Page limits are taken care of by the set_content method
def get_modifier(): def get_modifier():
if (not self.main_window.settings['double_page_mode'] if (not self.main_window.settings['double_page_mode']
or not self.are_we_doing_images_only): or not self.are_we_doing_images_only):
return 0 return 0
if (current_toc_index == 0 if (current_position == 0 or current_position % 2 == 0):
or current_toc_index % 2 == 0
or current_toc_index == max_toc_index):
return 0 return 0
if current_toc_index % 2 == 1:
if current_position % 2 == 1:
return direction return direction
self.main_window.bookToolBar.tocBox.setCurrentIndex( current_tab.set_content(
current_toc_index + direction + get_modifier()) current_position + direction + get_modifier(), True)
# Set page position depending on if the chapter number is increasing or decreasing # Set page position depending on if the chapter number is increasing or decreasing
if direction == 1 or was_button_pressed: if direction == 1 or was_button_pressed:
@@ -849,14 +846,27 @@ class PliantWidgetsCommonFunctions:
def generate_combo_box_action(self, contextMenu): def generate_combo_box_action(self, contextMenu):
contextMenu.addSeparator() contextMenu.addSeparator()
tocCombobox = QtWidgets.QComboBox() def set_toc_position(tocTree):
toc_data = [i[0] for i in self.pw.parent.metadata['content']] currentIndex = tocTree.currentIndex()
tocCombobox.addItems(toc_data) required_position = currentIndex.data(QtCore.Qt.UserRole)
tocCombobox.setCurrentIndex( self.pw.parent.set_content(required_position, True)
self.pw.main_window.bookToolBar.tocBox.currentIndex())
tocCombobox.currentIndexChanged.connect( # Create the Combobox / Treeview combination
self.pw.main_window.bookToolBar.tocBox.setCurrentIndex) tocComboBox = QtWidgets.QComboBox()
tocTree = QtWidgets.QTreeView()
tocComboBox.setView(tocTree)
tocComboBox.setModel(self.pw.parent.tocModel)
tocTree.setRootIsDecorated(False)
tocTree.setItemsExpandable(False)
tocTree.expandAll()
# Set the position of the QComboBox
self.pw.parent.set_tocBox_index(None, tocComboBox)
# Make clicking do something
tocComboBox.currentIndexChanged.connect(
lambda: set_toc_position(tocTree))
comboboxAction = QtWidgets.QWidgetAction(self.pw) comboboxAction = QtWidgets.QWidgetAction(self.pw)
comboboxAction.setDefaultWidget(tocCombobox) comboboxAction.setDefaultWidget(tocComboBox)
contextMenu.addAction(comboboxAction) contextMenu.addAction(comboboxAction)

View File

@@ -83,11 +83,11 @@ class ParseCOMIC:
return None return None
def get_contents(self): def get_contents(self):
file_settings = {'images_only': True} image_number = len(self.image_list)
contents = [(f'Page {count + 1}', i) for count, i in enumerate(self.image_list)] toc = [(1, f'Page {i + 1}', i + 1) for i in range(image_number)]
return contents, file_settings
# Return toc, content, images_only
return toc, self.image_list, True
def is_image(filename): def is_image(filename):
valid_image_extensions = ['.png', '.jpg', '.bmp'] valid_image_extensions = ['.png', '.jpg', '.bmp']

View File

@@ -63,6 +63,12 @@ class ParseEPUB:
self.book_ref.parse_toc() self.book_ref.parse_toc()
self.book_ref.parse_chapters(temp_dir=self.extract_path) self.book_ref.parse_chapters(temp_dir=self.extract_path)
file_settings = {
'images_only': False} toc = []
return self.book['book_list'], file_settings content = []
for count, i in enumerate(self.book['book_list']):
toc.append((1, i[0], count + 1))
content.append(i[1])
# Return toc, content, images_only
return toc, content, False

View File

@@ -60,6 +60,12 @@ class ParseFB2:
def get_contents(self): def get_contents(self):
os.makedirs(self.extract_path, exist_ok=True) # Manual creation is required here os.makedirs(self.extract_path, exist_ok=True) # Manual creation is required here
self.book_ref.parse_chapters(temp_dir=self.extract_path) self.book_ref.parse_chapters(temp_dir=self.extract_path)
file_settings = {
'images_only': False} toc = []
return self.book['book_list'], file_settings content = []
for count, i in enumerate(self.book['book_list']):
toc.append((1, i[0], count + 1))
content.append(i[1])
# Return toc, content, images_only
return toc, content, False

View File

@@ -73,24 +73,13 @@ class ParsePDF:
return tags # Fine if it returns None return tags # Fine if it returns None
def get_contents(self): def get_contents(self):
# Contents are to be returned as: content = list(range(self.book.pageCount))
# Level, Title, Page Number toc = self.book.getToC()
# Increasing the level number means the if not toc:
# title is one level up in the tree toc = [(1, f'Page {i + 1}', i + 1) for i in range(self.book.pageCount)]
# TODO # Return toc, content, images_only
# Better parsing of TOC return toc, content, True
# file_settings = {'images_only': True}
# contents = self.book.getToC()
# if not contents:
# contents = [
# (1, f'Page {i + 1}', i) for i in range(self.book.pageCount)]
# return contents, file_settings
file_settings = {'images_only': True}
contents = [(f'Page {i + 1}', i) for i in range(self.book.pageCount)]
return contents, file_settings
def render_pdf_page(page_data, for_cover=False): def render_pdf_page(page_data, for_cover=False):

View File

@@ -217,6 +217,8 @@ class BookSorter:
return return
if book_ref.book: if book_ref.book:
# TODO
# For the love of God clean this up. It's junk.
this_book = {} this_book = {}
this_book[file_md5] = { this_book[file_md5] = {
@@ -246,18 +248,13 @@ class BookSorter:
this_book[file_md5]['addition_mode'] = self.addition_mode this_book[file_md5]['addition_mode'] = self.addition_mode
if self.work_mode == 'reading': if self.work_mode == 'reading':
all_content = book_ref.get_contents() # All books must return the following list
# Indices are as described below
book_breakdown = book_ref.get_contents()
# get_contents() returns a tuple. Index 1 is a collection of toc = book_breakdown[0]
# special settings that depend on the kind of data being parsed. content = book_breakdown[1]
# Currently, this includes: images_only = book_breakdown[2]
# Only images included images_only BOOL Book contains only images
content = all_content[0]
images_only = all_content[1]['images_only']
if not content:
content = [('Invalid', 'Something went horribly wrong')]
book_data = self.database_entry_for_book(file_md5) book_data = self.database_entry_for_book(file_md5)
title = book_data[0] title = book_data[0]
@@ -272,6 +269,7 @@ class BookSorter:
this_book[file_md5]['position'] = position this_book[file_md5]['position'] = position
this_book[file_md5]['bookmarks'] = bookmarks this_book[file_md5]['bookmarks'] = bookmarks
this_book[file_md5]['toc'] = toc
this_book[file_md5]['content'] = content this_book[file_md5]['content'] = content
this_book[file_md5]['images_only'] = images_only this_book[file_md5]['images_only'] = images_only
this_book[file_md5]['cover'] = cover this_book[file_md5]['cover'] = cover

View File

@@ -222,15 +222,16 @@ class BackGroundTextSearch(QtCore.QThread):
# through it looking for hits # through it looking for hits
for i in self.search_content: for i in self.search_content:
chapter = i[0] chapter_title = i[0]
chapterDocument = QtGui.QTextDocument() chapterDocument = QtGui.QTextDocument()
chapterDocument.setHtml(i[1]) chapterDocument.setHtml(i[1])
chapter_number = i[2]
findFlags = QtGui.QTextDocument.FindFlags(0) findFlags = QtGui.QTextDocument.FindFlags(0)
if self.case_sensitive:
findFlags = findFlags | QtGui.QTextDocument.FindCaseSensitively
if self.match_words: if self.match_words:
findFlags = findFlags | QtGui.QTextDocument.FindWholeWords findFlags = findFlags | QtGui.QTextDocument.FindWholeWords
if self.case_sensitive:
findFlags = findFlags | QtGui.QTextDocument.FindCaseSensitively
findResultCursor = chapterDocument.find(self.search_text, 0, findFlags) findResultCursor = chapterDocument.find(self.search_text, 0, findFlags)
while not findResultCursor.isNull(): while not findResultCursor.isNull():
@@ -252,12 +253,13 @@ class BackGroundTextSearch(QtCore.QThread):
surrounding_text = replace_pattern.sub( surrounding_text = replace_pattern.sub(
f'<b>{self.search_text}</b>', surrounding_text) f'<b>{self.search_text}</b>', surrounding_text)
result_tuple = (
result_position, surrounding_text, self.search_text, chapter_number)
try: try:
self.search_results[chapter].append( self.search_results[chapter_title].append(result_tuple)
(result_position, surrounding_text, self.search_text))
except KeyError: except KeyError:
self.search_results[chapter] = [ self.search_results[chapter_title] = [result_tuple]
(result_position, surrounding_text, self.search_text)]
new_position = result_position + len(self.search_text) new_position = result_position + len(self.search_text)
findResultCursor = chapterDocument.find( findResultCursor = chapterDocument.find(

View File

@@ -289,11 +289,15 @@ class BookToolBar(QtWidgets.QToolBar):
for i in self.comicActions: for i in self.comicActions:
i.setVisible(False) i.setVisible(False)
# Sorter # Table of contents Combo Box
# Has to have a QTreeview associated with it
self.tocBox = FixedComboBox(self) self.tocBox = FixedComboBox(self)
self.tocBox.setObjectName('sortingBox')
self.tocBox.setToolTip( self.tocBox.setToolTip(
self._translate('BookToolBar', 'Table of Contents')) self._translate('BookToolBar', 'Table of Contents'))
self.tocTreeView = QtWidgets.QTreeView(self.tocBox)
self.tocBox.setView(self.tocTreeView)
self.tocTreeView.setItemsExpandable(False)
self.tocTreeView.setRootIsDecorated(False)
# All of these will be put after the spacer # All of these will be put after the spacer
# This means that the buttons in the left side of # This means that the buttons in the left side of
@@ -326,17 +330,16 @@ class BookToolBar(QtWidgets.QToolBar):
self.customize_view_off() self.customize_view_off()
def customize_view_on(self): def customize_view_on(self):
if self.parent().tabWidget.widget( images_only = self.parent().tabWidget.currentWidget().are_we_doing_images_only
self.parent().tabWidget.currentIndex()).metadata['images_only']:
# The following might seem redundant, # The following might seem redundant,
# but it's necessary for tab switching # but it's necessary for tab switching
if images_only:
for i in self.comicActions: for i in self.comicActions:
i.setVisible(True) i.setVisible(True)
for i in self.fontActions: for i in self.fontActions:
i.setVisible(False) i.setVisible(False)
else: else:
for i in self.fontActions: for i in self.fontActions:
i.setVisible(True) i.setVisible(True)
@@ -368,7 +371,6 @@ class LibraryToolBar(QtWidgets.QToolBar):
self.setIconSize(QtCore.QSize(22, 22)) self.setIconSize(QtCore.QSize(22, 22))
self.setFloatable(False) self.setFloatable(False)
self.setContextMenuPolicy(QtCore.Qt.PreventContextMenu) self.setContextMenuPolicy(QtCore.Qt.PreventContextMenu)
self.setObjectName("LibraryToolBar")
image_factory = self.window().QImageFactory image_factory = self.window().QImageFactory
@@ -443,7 +445,6 @@ class LibraryToolBar(QtWidgets.QToolBar):
self._translate('LibraryToolBar', 'Search for Title, Author, Tags...')) self._translate('LibraryToolBar', 'Search for Title, Author, Tags...'))
self.searchBar.setSizePolicy(sizePolicy) self.searchBar.setSizePolicy(sizePolicy)
self.searchBar.setContentsMargins(0, 0, 10, 0) self.searchBar.setContentsMargins(0, 0, 10, 0)
self.searchBar.setObjectName('searchBar')
# Sorter # Sorter
title_string = self._translate('LibraryToolBar', 'Title') title_string = self._translate('LibraryToolBar', 'Title')
@@ -458,8 +459,6 @@ class LibraryToolBar(QtWidgets.QToolBar):
self.sortingBox = FixedComboBox(self) self.sortingBox = FixedComboBox(self)
self.sortingBox.addItems(sorting_choices) self.sortingBox.addItems(sorting_choices)
self.sortingBox.setObjectName('sortingBox')
self.sortingBox.setSizePolicy(sizePolicy)
self.sortingBox.setMinimumContentsLength(10) self.sortingBox.setMinimumContentsLength(10)
self.sortingBox.setToolTip(self._translate('LibraryToolBar', 'Sort by')) self.sortingBox.setToolTip(self._translate('LibraryToolBar', 'Sort by'))
@@ -479,7 +478,7 @@ class FixedComboBox(QtWidgets.QComboBox):
def __init__(self, parent=None): def __init__(self, parent=None):
super(FixedComboBox, self).__init__(parent) super(FixedComboBox, self).__init__(parent)
screen_width = QtWidgets.QDesktopWidget().screenGeometry().width() screen_width = QtWidgets.QDesktopWidget().screenGeometry().width()
self.adjusted_size = screen_width // 4.8 self.adjusted_size = screen_width // 4.5
def sizeHint(self): def sizeHint(self):
# This and the one below should adjust to screen size # This and the one below should adjust to screen size
@@ -490,7 +489,7 @@ class FixedLineEdit(QtWidgets.QLineEdit):
def __init__(self, parent=None): def __init__(self, parent=None):
super(FixedLineEdit, self).__init__(parent) super(FixedLineEdit, self).__init__(parent)
screen_width = QtWidgets.QDesktopWidget().screenGeometry().width() screen_width = QtWidgets.QDesktopWidget().screenGeometry().width()
self.adjusted_size = screen_width // 4.8 self.adjusted_size = screen_width // 4.5
def sizeHint(self): def sizeHint(self):
return QtCore.QSize(self.adjusted_size, 22) return QtCore.QSize(self.adjusted_size, 22)

View File

@@ -39,7 +39,6 @@ class Tab(QtWidgets.QWidget):
self.setAttribute(QtCore.Qt.WA_DeleteOnClose) self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
self.first_run = True
self.main_window = main_window self.main_window = main_window
self.metadata = metadata # Save progress data into this dictionary self.metadata = metadata # Save progress data into this dictionary
self.are_we_doing_images_only = self.metadata['images_only'] self.are_we_doing_images_only = self.metadata['images_only']
@@ -51,7 +50,20 @@ class Tab(QtWidgets.QWidget):
self.metadata['last_accessed'] = QtCore.QDateTime().currentDateTime() self.metadata['last_accessed'] = QtCore.QDateTime().currentDateTime()
# Create relevant containers
if not self.metadata['annotations']:
self.metadata['annotations'] = {}
if not self.metadata['bookmarks']:
self.metadata['bookmarks'] = {}
# Generate toc Model
self.tocModel = QtGui.QStandardItemModel()
self.tocModel.setHorizontalHeaderLabels(('Table of Contents',))
self.generate_toc_model()
# Get the current position of the book
if self.metadata['position']: if self.metadata['position']:
# A book might have been marked read without being opened
if self.metadata['position']['is_read']: if self.metadata['position']['is_read']:
self.generate_position(True) self.generate_position(True)
current_chapter = self.metadata['position']['current_chapter'] current_chapter = self.metadata['position']['current_chapter']
@@ -59,53 +71,49 @@ class Tab(QtWidgets.QWidget):
self.generate_position() self.generate_position()
current_chapter = 1 current_chapter = 1
chapter_content = self.metadata['content'][current_chapter - 1][1]
# Create relevant containers
if not self.metadata['annotations']:
self.metadata['annotations'] = {}
# See bookmark availability
if not self.metadata['bookmarks']:
self.metadata['bookmarks'] = {}
# The content display widget is, by default a QTextBrowser. # The content display widget is, by default a QTextBrowser.
# In case the incoming data is only images # In case the incoming data is only images
# such as in the case of comic book files, # such as in the case of comic book files,
# we want a QGraphicsView widget doing all the heavy lifting # we want a QGraphicsView widget doing all the heavy lifting
# instead of a QTextBrowser # instead of a QTextBrowser
if self.are_we_doing_images_only: # Boolean
if self.are_we_doing_images_only:
self.contentView = PliantQGraphicsView( self.contentView = PliantQGraphicsView(
self.metadata['path'], self.main_window, self) self.metadata['path'], self.main_window, self)
self.contentView.loadImage(chapter_content)
else:
self.contentView = PliantQTextBrowser(self.main_window, self)
else:
self.contentView = PliantQTextBrowser(
self.main_window, self)
self.contentView.setReadOnly(True)
# TODO
# Change this when HTML navigation works
self.contentView.setOpenLinks(False)
# TODO
# Rename the .css files to something else here and keep
# a record of them .Currently, I'm just removing them
# for the sake of simplicity
relative_path_root = os.path.join( relative_path_root = os.path.join(
self.main_window.temp_dir.path(), self.metadata['hash']) self.main_window.temp_dir.path(), self.metadata['hash'])
relative_paths = [] relative_paths = []
for i in os.walk(relative_path_root): for i in os.walk(relative_path_root):
# TODO
# Rename the .css files to something else here and keep
# a record of them
# Currently, I'm just removing them for the sake of simplicity
for j in i[2]: for j in i[2]:
file_extension = os.path.splitext(j)[1] file_extension = os.path.splitext(j)[1]
if file_extension == '.css': if file_extension == '.css':
file_path = os.path.join(i[0], j) file_path = os.path.join(i[0], j)
os.remove(file_path) os.remove(file_path)
relative_paths.append(os.path.join(relative_path_root, i[0])) relative_paths.append(os.path.join(relative_path_root, i[0]))
self.contentView.setSearchPaths(relative_paths) self.contentView.setSearchPaths(relative_paths)
self.contentView.setOpenLinks(False) # TODO Change this when HTML navigation works
self.contentView.setHtml(chapter_content)
self.contentView.setReadOnly(True)
self.hiddenButton = QtWidgets.QToolButton(self) self.hiddenButton = QtWidgets.QToolButton(self)
self.hiddenButton.setVisible(False) self.hiddenButton.setVisible(False)
self.hiddenButton.clicked.connect(self.set_cursor_position) self.hiddenButton.clicked.connect(self.set_cursor_position)
# All content must be set through this function
self.set_content(current_chapter, True)
if not self.are_we_doing_images_only:
# Setting this later breaks cursor positioning for search results
self.hiddenButton.animateClick(50) self.hiddenButton.animateClick(50)
# Load annotations for current content # Load annotations for current content
@@ -414,6 +422,45 @@ class Tab(QtWidgets.QWidget):
QtGui.QKeySequence('Ctrl+F'), self.contentView) QtGui.QKeySequence('Ctrl+F'), self.contentView)
ksToggleSearch.activated.connect(lambda: self.toggle_side_dock(2)) ksToggleSearch.activated.connect(lambda: self.toggle_side_dock(2))
def generate_toc_model(self):
# The toc list is:
# 0: Level
# 1: Title
# 2: Chapter content / page number
# pprint it out to get a better idea of structure
toc = self.metadata['toc']
parent_list = []
for i in toc:
item = QtGui.QStandardItem()
item.setText(i[1])
item.setData(i[2], QtCore.Qt.UserRole)
item.setData(i[1], QtCore.Qt.UserRole + 1)
current_level = i[0]
if current_level == 1:
self.tocModel.appendRow(item)
parent_list.clear()
parent_list.append(item)
else:
parent_list[current_level - 2].appendRow(item)
try:
next_level = toc[toc.index(i) + 1][0]
if next_level > current_level:
parent_list.append(item)
if next_level < current_level:
level_difference = current_level - next_level
parent_list = parent_list[:-level_difference]
except IndexError:
pass
# This is needed to be able to have the toc Combobox
# jump to the correct position in the book when it is
# first opened
self.main_window.bookToolBar.tocBox.setModel(self.tocModel)
self.main_window.bookToolBar.tocTreeView.expandAll()
def go_fullscreen(self): def go_fullscreen(self):
# To allow toggles to function # To allow toggles to function
# properly after the fullscreening # properly after the fullscreening
@@ -434,7 +481,7 @@ class Tab(QtWidgets.QWidget):
self.main_window.hide() self.main_window.hide()
if not self.are_we_doing_images_only: if not self.are_we_doing_images_only:
self.hiddenButton.animateClick(100) self.hiddenButton.animateClick(50)
self.mouse_hide_timer.start(2000) self.mouse_hide_timer.start(2000)
self.is_fullscreen = True self.is_fullscreen = True
@@ -472,9 +519,27 @@ class Tab(QtWidgets.QWidget):
self.mouse_hide_timer.start(2000) self.mouse_hide_timer.start(2000)
self.contentView.setFocus() self.contentView.setFocus()
def change_chapter_tocBox(self): def set_content(self, required_position, tocBox_readjust=False):
chapter_number = self.main_window.bookToolBar.tocBox.currentIndex() # All content changes must come through here
required_content = self.metadata['content'][chapter_number][1] # This function will decide how to relate
# entries in the toc to the actual content
# Do not allow cycling below page 1
# Required position goes to -1 in double page view
if required_position <= 0:
return
# Set the required page to the corresponding index
# For images this is simply a page number
# For text based books, this is the entire text of the chapter
try:
required_content = self.metadata['content'][required_position - 1]
except IndexError:
return # Do not allow cycling beyond last page
# Update the metadata dictionary to save position
self.metadata['position']['current_chapter'] = required_position
self.metadata['position']['is_read'] = False
if self.are_we_doing_images_only: if self.are_we_doing_images_only:
self.contentView.loadImage(required_content) self.contentView.loadImage(required_content)
@@ -482,9 +547,66 @@ class Tab(QtWidgets.QWidget):
self.contentView.clear() self.contentView.clear()
self.contentView.setHtml(required_content) self.contentView.setHtml(required_content)
self.contentView.common_functions.load_annotations(chapter_number + 1) # Set the contentview to look the way God intended
self.main_window.profile_functions.format_contentView()
self.contentView.common_functions.load_annotations(required_position)
def format_view(self, font, font_size, foreground, # Change the index of the tocBox. This is manual and each function
# that calls set_position must specify if it needs this adjustment
if tocBox_readjust:
self.set_tocBox_index(required_position, None)
def set_tocBox_index(self, current_position=None, tocBox=None):
# Get current position from the metadata dictionary
# in case it isn't specified
if not current_position:
current_position = self.metadata['position']['current_chapter']
# Just look at the variable names. They're practically sentences.
positions_available_in_toc = [i[2] for i in self.metadata['toc']]
try:
position_reference_index = positions_available_in_toc.index(
current_position)
position_reference = positions_available_in_toc[
position_reference_index]
except ValueError: # No specific corresponding value was found
# Going for nearest preceding neighbor
for count, i in enumerate(positions_available_in_toc):
try:
if (positions_available_in_toc[count] <
current_position <
positions_available_in_toc[count + 1]):
position_reference = i
break
except IndexError: # Set to the last chapter
position_reference = positions_available_in_toc[-1]
# Match the position reference to the corresponding
# index in the QTreeView / QCombobox
try:
matchingIndex = self.tocModel.match(
self.tocModel.index(0, 0),
QtCore.Qt.UserRole,
position_reference,
2, QtCore.Qt.MatchRecursive)[0]
except IndexError:
return
if not tocBox:
tocBox = self.main_window.bookToolBar.tocBox
# The following sets the QCombobox index according
# to the index found above.
tocBox.blockSignals(True)
currentRootModelIndex = tocBox.rootModelIndex()
tocBox.setRootModelIndex(matchingIndex.parent())
tocBox.setCurrentIndex(matchingIndex.row())
tocBox.setRootModelIndex(currentRootModelIndex)
tocBox.blockSignals(False)
def format_view(
self, font, font_size, foreground,
background, padding, line_spacing, background, padding, line_spacing,
text_alignment): text_alignment):
@@ -543,14 +665,15 @@ class Tab(QtWidgets.QWidget):
break break
def generate_annotation_model(self): def generate_annotation_model(self):
# TODO
# Annotation previews will require creation of a
# QStyledItemDelegate
saved_annotations = self.main_window.settings['annotations'] saved_annotations = self.main_window.settings['annotations']
if not saved_annotations: if not saved_annotations:
return return
# Create annotation model # Create annotation model
# TODO
# Annotation previews will require creation of a
# QStyledItemDelegate
for i in saved_annotations: for i in saved_annotations:
item = QtGui.QStandardItem() item = QtGui.QStandardItem()
item.setText(i['name']) item.setText(i['name'])
@@ -581,7 +704,7 @@ class Tab(QtWidgets.QWidget):
description, chapter, cursor_position, identifier, True) description, chapter, cursor_position, identifier, True)
def add_bookmark_to_model( def add_bookmark_to_model(
self, description, chapter, cursor_position, self, description, chapter_number, cursor_position,
identifier, new_bookmark=False): identifier, new_bookmark=False):
def edit_new_bookmark(parent_item): def edit_new_bookmark(parent_item):
@@ -596,7 +719,7 @@ class Tab(QtWidgets.QWidget):
bookmark = QtGui.QStandardItem() bookmark = QtGui.QStandardItem()
bookmark.setData(False, QtCore.Qt.UserRole + 10) # Is Parent bookmark.setData(False, QtCore.Qt.UserRole + 10) # Is Parent
bookmark.setData(chapter, QtCore.Qt.UserRole) # Chapter name bookmark.setData(chapter_number, QtCore.Qt.UserRole) # Chapter number
bookmark.setData(cursor_position, QtCore.Qt.UserRole + 1) # Cursor Position bookmark.setData(cursor_position, QtCore.Qt.UserRole + 1) # Cursor Position
bookmark.setData(identifier, QtCore.Qt.UserRole + 2) # Identifier bookmark.setData(identifier, QtCore.Qt.UserRole + 2) # Identifier
bookmark.setData(description, QtCore.Qt.DisplayRole) # Description bookmark.setData(description, QtCore.Qt.DisplayRole) # Description
@@ -604,21 +727,20 @@ class Tab(QtWidgets.QWidget):
for i in range(self.bookmarkModel.rowCount()): for i in range(self.bookmarkModel.rowCount()):
parentIndex = self.bookmarkModel.index(i, 0) parentIndex = self.bookmarkModel.index(i, 0)
parent_chapter = parentIndex.data(QtCore.Qt.UserRole) parent_chapter = parentIndex.data(QtCore.Qt.UserRole)
if parent_chapter == chapter: if parent_chapter == chapter_number:
bookmarkParent = self.bookmarkModel.itemFromIndex(parentIndex) bookmarkParent = self.bookmarkModel.itemFromIndex(parentIndex)
bookmarkParent.appendRow(bookmark) bookmarkParent.appendRow(bookmark)
if new_bookmark: if new_bookmark:
edit_new_bookmark(bookmarkParent) edit_new_bookmark(bookmarkParent)
return return
# In case no parent item exists # In case no parent item exists
bookmarkParent = QtGui.QStandardItem() bookmarkParent = QtGui.QStandardItem()
bookmarkParent.setData(True, QtCore.Qt.UserRole + 10) # Is Parent bookmarkParent.setData(True, QtCore.Qt.UserRole + 10) # Is Parent
bookmarkParent.setFlags(bookmarkParent.flags() & ~QtCore.Qt.ItemIsEditable) # Is Editable bookmarkParent.setFlags(bookmarkParent.flags() & ~QtCore.Qt.ItemIsEditable) # Is Editable
chapter_name = self.metadata['content'][chapter - 1][0] # Chapter Name chapter_name = [i[1] for i in self.metadata['toc'] if i[2] == chapter_number][0]
bookmarkParent.setData(chapter_name, QtCore.Qt.DisplayRole) bookmarkParent.setData(chapter_name, QtCore.Qt.DisplayRole)
bookmarkParent.setData(chapter, QtCore.Qt.UserRole) # Chapter Number bookmarkParent.setData(chapter_number, QtCore.Qt.UserRole)
bookmarkParent.appendRow(bookmark) bookmarkParent.appendRow(bookmark)
self.bookmarkModel.appendRow(bookmarkParent) self.bookmarkModel.appendRow(bookmarkParent)
@@ -632,13 +754,13 @@ class Tab(QtWidgets.QWidget):
is_parent = self.bookmarkProxyModel.data(index, QtCore.Qt.UserRole + 10) is_parent = self.bookmarkProxyModel.data(index, QtCore.Qt.UserRole + 10)
if is_parent: if is_parent:
chapter_number = self.bookmarkProxyModel.data(index, QtCore.Qt.UserRole) chapter_number = self.bookmarkProxyModel.data(index, QtCore.Qt.UserRole)
self.main_window.bookToolBar.tocBox.setCurrentIndex(chapter_number - 1) self.set_content(chapter_number, True)
return return
chapter = self.bookmarkProxyModel.data(index, QtCore.Qt.UserRole) chapter = self.bookmarkProxyModel.data(index, QtCore.Qt.UserRole)
cursor_position = self.bookmarkProxyModel.data(index, QtCore.Qt.UserRole + 1) cursor_position = self.bookmarkProxyModel.data(index, QtCore.Qt.UserRole + 1)
self.main_window.bookToolBar.tocBox.setCurrentIndex(chapter - 1) self.set_content(chapter, True)
if not self.are_we_doing_images_only: if not self.are_we_doing_images_only:
self.set_cursor_position(cursor_position) self.set_cursor_position(cursor_position)
@@ -646,13 +768,14 @@ class Tab(QtWidgets.QWidget):
self.bookmarkModel = QtGui.QStandardItemModel(self) self.bookmarkModel = QtGui.QStandardItemModel(self)
if self.main_window.settings['toc_with_bookmarks']: if self.main_window.settings['toc_with_bookmarks']:
for chapter_number, i in enumerate(self.metadata['content']): pass
chapterItem = QtGui.QStandardItem() # for chapter_number, i in enumerate(self.metadata['content']):
chapterItem.setData(i[0], QtCore.Qt.DisplayRole) # Display name # chapterItem = QtGui.QStandardItem()
chapterItem.setData(chapter_number + 1, QtCore.Qt.UserRole) # Chapter Number # chapterItem.setData(i[0], QtCore.Qt.DisplayRole) # Display name
chapterItem.setData(True, QtCore.Qt.UserRole + 10) # Is Parent # chapterItem.setData(chapter_number + 1, QtCore.Qt.UserRole) # Chapter Number
chapterItem.setFlags(chapterItem.flags() & ~QtCore.Qt.ItemIsEditable) # Is Editable # chapterItem.setData(True, QtCore.Qt.UserRole + 10) # Is Parent
self.bookmarkModel.appendRow(chapterItem) # chapterItem.setFlags(chapterItem.flags() & ~QtCore.Qt.ItemIsEditable) # Is Editable
# self.bookmarkModel.appendRow(chapterItem)
for i in self.metadata['bookmarks'].items(): for i in self.metadata['bookmarks'].items():
description = i[1]['description'] description = i[1]['description']
@@ -708,10 +831,20 @@ class Tab(QtWidgets.QWidget):
self.bookmarkModel.removeRow(parent_index.row()) self.bookmarkModel.removeRow(parent_index.row())
def set_search_options(self): def set_search_options(self):
search_content = ( def generate_title_content_pair(required_chapters):
self.metadata['content'][self.main_window.bookToolBar.tocBox.currentIndex()],) title_content_list = []
for i in self.metadata['toc']:
if i[2] in required_chapters:
title_content_list.append(
(i[1], self.metadata['content'][i[2] - 1], i[2]))
return title_content_list
# Select either the current chapter or all chapters
# Function name is descriptive
chapter_numbers = (self.metadata['position']['current_chapter'],)
if self.searchBookButton.isChecked(): if self.searchBookButton.isChecked():
search_content = self.metadata['content'] chapter_numbers = [i + 1 for i in range(len(self.metadata['content']))]
search_content = generate_title_content_pair(chapter_numbers)
self.searchThread.set_search_options( self.searchThread.set_search_options(
search_content, search_content,
@@ -727,13 +860,11 @@ class Tab(QtWidgets.QWidget):
parentItem = QtGui.QStandardItem() parentItem = QtGui.QStandardItem()
parentItem.setData(True, QtCore.Qt.UserRole) # Is parent? parentItem.setData(True, QtCore.Qt.UserRole) # Is parent?
parentItem.setData(i, QtCore.Qt.UserRole + 3) # Display text for label parentItem.setData(i, QtCore.Qt.UserRole + 3) # Display text for label
chapter_index = self.main_window.bookToolBar.tocBox.findText(
i, QtCore.Qt.MatchExactly)
for j in search_results[i]: for j in search_results[i]:
childItem = QtGui.QStandardItem(parentItem) childItem = QtGui.QStandardItem(parentItem)
childItem.setData(False, QtCore.Qt.UserRole) # Is parent? childItem.setData(False, QtCore.Qt.UserRole) # Is parent?
childItem.setData(chapter_index, QtCore.Qt.UserRole + 1) # Chapter index childItem.setData(j[3], QtCore.Qt.UserRole + 1) # Chapter index
childItem.setData(j[0], QtCore.Qt.UserRole + 2) # Cursor Position childItem.setData(j[0], QtCore.Qt.UserRole + 2) # Cursor Position
childItem.setData(j[1], QtCore.Qt.UserRole + 3) # Display text for label childItem.setData(j[1], QtCore.Qt.UserRole + 3) # Display text for label
childItem.setData(j[2], QtCore.Qt.UserRole + 4) # Search term childItem.setData(j[2], QtCore.Qt.UserRole + 4) # Search term
@@ -743,6 +874,12 @@ class Tab(QtWidgets.QWidget):
self.searchResultsTreeView.setModel(self.searchResultsModel) self.searchResultsTreeView.setModel(self.searchResultsModel)
self.searchResultsTreeView.expandToDepth(1) self.searchResultsTreeView.expandToDepth(1)
# Reset stylesheet in case something is found
if search_results:
self.searchLineEdit.setStyleSheet(
QtWidgets.QLineEdit.styleSheet(self))
# Or set to Red in case nothing is found
if not search_results and len(self.searchLineEdit.text()) > 2: if not search_results and len(self.searchLineEdit.text()) > 2:
self.searchLineEdit.setStyleSheet("QLineEdit {color: red;}") self.searchLineEdit.setStyleSheet("QLineEdit {color: red;}")
@@ -773,11 +910,11 @@ class Tab(QtWidgets.QWidget):
if is_parent: if is_parent:
return return
chapter_index = self.searchResultsModel.data(index, QtCore.Qt.UserRole + 1) chapter_number = self.searchResultsModel.data(index, QtCore.Qt.UserRole + 1)
cursor_position = self.searchResultsModel.data(index, QtCore.Qt.UserRole + 2) cursor_position = self.searchResultsModel.data(index, QtCore.Qt.UserRole + 2)
search_term = self.searchResultsModel.data(index, QtCore.Qt.UserRole + 4) search_term = self.searchResultsModel.data(index, QtCore.Qt.UserRole + 4)
self.main_window.bookToolBar.tocBox.setCurrentIndex(chapter_index) self.set_content(chapter_number, True)
if not self.are_we_doing_images_only: if not self.are_we_doing_images_only:
self.set_cursor_position( self.set_cursor_position(
cursor_position, len(search_term)) cursor_position, len(search_term))