From 7874838550f42225df5cba42bd79086ed2bb7969 Mon Sep 17 00:00:00 2001 From: BasioMeusPuga Date: Sun, 25 Feb 2018 22:05:37 +0530 Subject: [PATCH] Bookmark creation and navigation, Cleanup --- TODO | 9 ++--- __main__.py | 5 +-- database.py | 15 ++++---- models.py | 9 ++--- sorter.py | 10 +++--- threaded.py | 6 ++-- widgets.py | 100 +++++++++++++++++++++++++++++++++++++++++++--------- 7 files changed, 112 insertions(+), 42 deletions(-) diff --git a/TODO b/TODO index 9e9b66a..1e3287f 100644 --- a/TODO +++ b/TODO @@ -43,17 +43,17 @@ TODO ✓ View and hide toolbar actions in a list ✓ Line spacing ✓ Record progress + Bookmarks + Set context menu for definitions and the like Search document using QTextCursor? - Use embedded fonts Cache multiple images Graphical themes Comic view keyboard shortcuts Comic view modes Continuous paging Double pages - Bookmarks Pagination - Set context menu for definitions and the like + Use embedded fonts Scrolling: Smooth / By Line Filetypes: ✓ cbz, cbr support @@ -72,4 +72,5 @@ TODO ✓ Include icons for emblems Shift to logging instead of print statements Bugs: - If there are files open and the database is deleted, TypeErrors result \ No newline at end of file + If there are files open and the database is deleted, TypeErrors result + Closing a fullscreened contentView does not save settings \ No newline at end of file diff --git a/__main__.py b/__main__.py index 43dfb7f..f206b5a 100755 --- a/__main__.py +++ b/__main__.py @@ -334,7 +334,9 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow): item.setData(True, QtCore.Qt.UserRole + 8) def test_function(self): - print('Caesar si viveret, ad remum dareris') + # print('Caesar si viveret, ad remum dareris') + if self.tabWidget.currentIndex() != 0: + self.tabWidget.widget(self.tabWidget.currentIndex()).add_bookmark() def resizeEvent(self, event=None): if event: @@ -376,7 +378,6 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow): # Remember file addition modality # If a file is added from here, it should not be removed # from the libary in case of a database refresh - # Individually added files are not subject to library filtering opened_files = QtWidgets.QFileDialog.getOpenFileNames( self, 'Open file', self.settings['last_open_path'], diff --git a/database.py b/database.py index df55bc4..b4eeae1 100644 --- a/database.py +++ b/database.py @@ -19,7 +19,6 @@ import os import pickle import sqlite3 - from PyQt5 import QtCore @@ -163,28 +162,30 @@ class DatabaseFunctions: return None except (KeyError, sqlite3.OperationalError): - print('SQLite is in rebellion, Commander') + print('Commander, SQLite is in rebellion @ data fetching handling') self.close_database() - def modify_position(self, hash_position_last_accessed): - for i in hash_position_last_accessed: + def modify_positional_data(self, positional_data): + for i in positional_data: file_hash = i[0] position = i[1] last_accessed = i[2] + bookmarks = i[3] position_bin = sqlite3.Binary(pickle.dumps(position)) last_accessed_bin = sqlite3.Binary(pickle.dumps(last_accessed)) + bookmarks_bin = sqlite3.Binary(pickle.dumps(bookmarks)) sql_command = ( - "UPDATE books SET Position = ?, LastAccessed = ? WHERE Hash = ?") + "UPDATE books SET Position = ?, LastAccessed = ?, Bookmarks = ? WHERE Hash = ?") try: self.database.execute( sql_command, - [position_bin, last_accessed_bin, file_hash]) + [position_bin, last_accessed_bin, bookmarks_bin, file_hash]) except sqlite3.OperationalError: - print('SQLite is in rebellion, Commander') + print('Commander, SQLite is in rebellion @ positional data handling') return self.database.commit() diff --git a/models.py b/models.py index c3a7801..5261141 100644 --- a/models.py +++ b/models.py @@ -27,12 +27,12 @@ class ItemProxyModel(QtCore.QSortFilterProxyModel): super(ItemProxyModel, self).__init__(parent) self.filter_text = None self.active_library_filters = None - self.sorting_position = None + self.sorting_box_position = None - def setFilterParams(self, filter_text, active_library_filters, sorting_position): + def setFilterParams(self, filter_text, active_library_filters, sorting_box_position): self.filter_text = filter_text self.active_library_filters = [i.lower() for i in active_library_filters] - self.sorting_position = sorting_position + self.sorting_box_position = sorting_box_position def filterAcceptsRow(self, row, parent): model = self.sourceModel() @@ -46,7 +46,8 @@ class ItemProxyModel(QtCore.QSortFilterProxyModel): directory_tags = model.data(this_index, QtCore.Qt.UserRole + 11) last_accessed = model.data(this_index, QtCore.Qt.UserRole + 12) - if self.sorting_position == 4 and not last_accessed: + # Hide untouched files when sorting by last accessed + if self.sorting_box_position == 4 and not last_accessed: return False if self.active_library_filters: diff --git a/sorter.py b/sorter.py index 5888ce4..0de5aff 100644 --- a/sorter.py +++ b/sorter.py @@ -110,13 +110,14 @@ class BookSorter: def database_entry_for_book(self, file_hash): database_return = database.DatabaseFunctions( self.database_path).fetch_data( - ('DateAdded', 'Position', 'Bookmarks'), + ('Position', 'Bookmarks'), 'books', {'Hash': file_hash}, 'EQUALS')[0] book_data = [] for i in database_return: + # All of these values are pickled and stored if i: book_data.append(pickle.loads(i)) else: @@ -214,12 +215,9 @@ class BookSorter: content['Invalid'] = 'Possible Parse Error' book_data = self.database_entry_for_book(file_md5) + position = book_data[0] + bookmarks = book_data[1] - date_added = book_data[0] - position = book_data[1] - bookmarks = book_data[2] - - this_book[file_md5]['date_added'] = date_added this_book[file_md5]['position'] = position this_book[file_md5]['bookmarks'] = bookmarks this_book[file_md5]['content'] = content diff --git a/threaded.py b/threaded.py index b6c1039..10143c4 100644 --- a/threaded.py +++ b/threaded.py @@ -37,11 +37,13 @@ class BackGroundTabUpdate(QtCore.QThread): file_hash = i['hash'] position = i['position'] last_accessed = i['last_accessed'] + bookmarks = i['bookmarks'] - hash_position_pairs.append([file_hash, position, last_accessed]) + hash_position_pairs.append( + [file_hash, position, last_accessed, bookmarks]) database.DatabaseFunctions( - self.database_path).modify_position(hash_position_pairs) + self.database_path).modify_positional_data(hash_position_pairs) class BackGroundBookAddition(QtCore.QThread): diff --git a/widgets.py b/widgets.py index 3baf294..14864f5 100644 --- a/widgets.py +++ b/widgets.py @@ -386,7 +386,6 @@ class Tab(QtWidgets.QWidget): self.metadata['last_accessed'] = QtCore.QDateTime().currentDateTime() position = self.metadata['position'] - if position: current_chapter = position['current_chapter'] else: @@ -420,10 +419,10 @@ class Tab(QtWidgets.QWidget): self.contentView.setHtml(chapter_content) self.contentView.setReadOnly(True) - temp_hidden_button = QtWidgets.QToolButton(self) - temp_hidden_button.setVisible(False) - temp_hidden_button.clicked.connect(self.set_scroll_value) - temp_hidden_button.animateClick(100) + tempHiddenButton = QtWidgets.QToolButton(self) + tempHiddenButton.setVisible(False) + tempHiddenButton.clicked.connect(self.set_scroll_value) + tempHiddenButton.animateClick(100) # The following are common to both the text browser and # the graphics view @@ -437,12 +436,18 @@ class Tab(QtWidgets.QWidget): self.dockWidget = QtWidgets.QDockWidget(self) self.dockWidget.setFeatures(QtWidgets.QDockWidget.DockWidgetClosable) self.dockWidget.setFloating(False) - self.dockListWidget = QtWidgets.QListWidget() - self.dockListWidget.setResizeMode(QtWidgets.QListWidget.Adjust) - self.dockListWidget.setMaximumWidth(350) - self.dockWidget.setWidget(self.dockListWidget) self.dockWidget.hide() + self.dockListView = QtWidgets.QListView(self.dockWidget) + self.dockListView.setResizeMode(QtWidgets.QListWidget.Adjust) + self.dockListView.setMaximumWidth(350) + self.dockListView.clicked.connect(self.navigate_to_bookmark) + self.dockWidget.setWidget(self.dockListView) + + self.bookmark_model = QtGui.QStandardItemModel() + self.generate_bookmark_model() + self.dockListView.setModel(self.bookmark_model) + self.generate_keyboard_shortcuts() self.horzLayout.addWidget(self.contentView) @@ -470,21 +475,25 @@ class Tab(QtWidgets.QWidget): self.window().lib_ref.view_model.setData( matching_item[0], self.metadata['last_accessed'], QtCore.Qt.UserRole + 12) - def set_scroll_value(self, switch_widgets=True): + def set_scroll_value(self, switch_widgets=True, search_data=None): if switch_widgets: previous_widget = self.window().tabWidget.currentWidget() self.window().tabWidget.setCurrentWidget(self) - scroll_position = ( - self.metadata['position']['scroll_value'] * - self.contentView.verticalScrollBar().maximum()) + scroll_value = self.metadata['position']['scroll_value'] + if search_data: + scroll_value = search_data[0] # Scroll a little ahead # This avoids confusion with potentially duplicate phrases # And the found result is at the top of the window - self.contentView.verticalScrollBar().setValue(scroll_position * 1.1) + scroll_position = scroll_value * self.contentView.verticalScrollBar().maximum() * 1.1 + self.contentView.verticalScrollBar().setValue(scroll_position) last_visible_text = self.metadata['position']['last_visible_text'] + if search_data: + last_visible_text = search_data[1] + if last_visible_text: self.contentView.find(last_visible_text) @@ -501,7 +510,6 @@ class Tab(QtWidgets.QWidget): # Calculate lines to incorporate into progress self.metadata['position'] = { 'current_chapter': 1, - 'current_line': 0, 'total_chapters': total_chapters, 'scroll_value': 0, 'last_visible_text': None} @@ -579,6 +587,10 @@ class Tab(QtWidgets.QWidget): block_format.setLineHeight( line_spacing, QtGui.QTextBlockFormat.ProportionalHeight) + # TODO + # Give options for alignment + # block_format.setAlignment(QtCore.Qt.AlignJustify) + # Also for padding # Using setViewPortMargins for this disables scrolling in the margins block_format.setLeftMargin(padding) @@ -605,6 +617,47 @@ class Tab(QtWidgets.QWidget): else: self.dockWidget.show() + def add_bookmark(self): + chapter, scroll_position, visible_text = self.contentView.record_scroll_position(True) + description = 'New bookmark' + search_data = (scroll_position, visible_text) + + self.metadata['bookmarks'].append([ + chapter, search_data, description]) + self.add_bookmark_to_model(description, chapter, search_data) + + def generate_bookmark_model(self): + bookmarks = self.metadata['bookmarks'] + + if not bookmarks: + self.metadata['bookmarks'] = [] + return + + # TODO + # Replace this with proxy model sorting + bookmarks.sort(key=lambda x: x[0]) + + for i in bookmarks: + self.add_bookmark_to_model(i[2], i[0], i[1]) + + def add_bookmark_to_model(self, description, chapter, search_data): + bookmark = QtGui.QStandardItem() + bookmark.setData(description, QtCore.Qt.DisplayRole) + bookmark.setData(chapter, QtCore.Qt.UserRole) + bookmark.setData(search_data, QtCore.Qt.UserRole + 1) + + self.bookmark_model.appendRow(bookmark) + + def navigate_to_bookmark(self, index): + if not index.isValid(): + return + + chapter = self.bookmark_model.data(index, QtCore.Qt.UserRole) + search_data = self.bookmark_model.data(index, QtCore.Qt.UserRole + 1) + + self.window().bookToolBar.tocBox.setCurrentIndex(chapter - 1) + self.set_scroll_value(False, search_data) + def hide_mouse(self): self.contentView.setCursor(QtCore.Qt.BlankCursor) @@ -629,11 +682,18 @@ class PliantQGraphicsView(QtWidgets.QGraphicsView): self.image_pixmap = None self.ignore_wheel_event = False self.ignore_wheel_event_number = 0 + self.setDragMode(QtWidgets.QGraphicsView.ScrollHandDrag) self.common_functions = PliantWidgetsCommonFunctions( self, self.main_window) self.setMouseTracking(True) def loadImage(self, image_path): + # TODO + # Cache 4 images + # For single page view: 1 before, 2 after + # For double page view: 1 before, 1 after + # Image panning with mouse + self.image_pixmap = QtGui.QPixmap() self.image_pixmap.load(image_path) self.resizeEvent() @@ -731,7 +791,7 @@ class PliantQTextBrowser(QtWidgets.QTextBrowser): else: QtWidgets.QTextEdit.keyPressEvent(self, event) - def record_scroll_position(self): + def record_scroll_position(self, return_as_bookmark=False): vertical = self.verticalScrollBar().value() maximum = self.verticalScrollBar().maximum() @@ -747,7 +807,13 @@ class PliantQTextBrowser(QtWidgets.QTextBrowser): if len(visible_text) > 50: visible_text = visible_text[:51] - self.parent.metadata['position']['last_visible_text'] = visible_text + + if return_as_bookmark: + return (self.parent.metadata['position']['current_chapter'], + self.parent.metadata['position']['scroll_value'], + visible_text) + else: + self.parent.metadata['position']['last_visible_text'] = visible_text # def mouseMoveEvent(self, event): # TODO