From 0b64fc3e8d78d44ca3f42b89b2407c4c17684acd Mon Sep 17 00:00:00 2001 From: BasioMeusPuga Date: Wed, 21 Feb 2018 01:05:15 +0530 Subject: [PATCH] Implement sorting by last read --- TODO | 1 + __main__.py | 18 +++++++++++++----- database.py | 18 +++++++++++------- library.py | 16 ++++++++++------ models.py | 8 +++++++- threaded.py | 4 +++- widgets.py | 31 ++++++++++++++++++++++--------- 7 files changed, 67 insertions(+), 29 deletions(-) diff --git a/TODO b/TODO index eec7891..9e9b66a 100644 --- a/TODO +++ b/TODO @@ -45,6 +45,7 @@ TODO ✓ Record progress Search document using QTextCursor? Use embedded fonts + Cache multiple images Graphical themes Comic view keyboard shortcuts Comic view modes diff --git a/__main__.py b/__main__.py index ec1eda5..43dfb7f 100755 --- a/__main__.py +++ b/__main__.py @@ -16,9 +16,6 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# TODO -# Consider using sender().text() instead of sender().objectName() - import os import sys import hashlib @@ -159,6 +156,7 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow): self.addToolBar(self.bookToolBar) # Make the correct toolbar visible + self.current_tab = self.tabWidget.currentIndex() self.tab_switch() self.tabWidget.currentChanged.connect(self.tab_switch) @@ -481,6 +479,15 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow): self.resizeEvent() def tab_switch(self): + try: + if self.current_tab != 0: + self.tabWidget.widget( + self.current_tab).update_last_accessed_time() + except AttributeError: + pass + + self.current_tab = self.tabWidget.currentIndex() + if self.tabWidget.currentIndex() == 0: self.resizeEvent() @@ -539,6 +546,7 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow): self.database_path, [tab_metadata]) self.thread.start() + self.tabWidget.widget(tab_index).update_last_accessed_time() self.tabWidget.removeTab(tab_index) def set_toc_position(self, event=None): @@ -681,7 +689,7 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow): self.tabWidget.setCurrentIndex(self.tabWidget.count() - 1) finishing_touches() - + # TODO # def dropEvent @@ -892,7 +900,7 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow): checked = [i for i in directory_list if i[3] == QtCore.Qt.Checked] filter_list = list(map(generate_name, checked)) filter_list.sort() - filter_list.append('Manually added') + filter_list.append('Manually Added') filter_actions = [QtWidgets.QAction(i, self.library_filter_menu) for i in filter_list] filter_all = QtWidgets.QAction('All', self.library_filter_menu) diff --git a/database.py b/database.py index 7081304..df55bc4 100644 --- a/database.py +++ b/database.py @@ -38,8 +38,8 @@ class DatabaseInit: # addition mode self.database.execute( "CREATE TABLE books \ - (id INTEGER PRIMARY KEY, Title TEXT, Author TEXT, Year INTEGER, DateAdded TEXT, \ - Path TEXT, Position BLOB, ISBN TEXT, Tags TEXT, Hash TEXT, \ + (id INTEGER PRIMARY KEY, Title TEXT, Author TEXT, Year INTEGER, DateAdded BLOB, \ + Path TEXT, Position BLOB, ISBN TEXT, Tags TEXT, Hash TEXT, LastAccessed BLOB,\ Bookmarks BLOB, CoverImage BLOB)") # CheckState is the standard QtCore.Qt.Checked / Unchecked @@ -167,18 +167,22 @@ class DatabaseFunctions: self.close_database() - def modify_position(self, hash_position_pairs): - for i in hash_position_pairs: + def modify_position(self, hash_position_last_accessed): + for i in hash_position_last_accessed: file_hash = i[0] position = i[1] + last_accessed = i[2] - pickled_position = pickle.dumps(position) + position_bin = sqlite3.Binary(pickle.dumps(position)) + last_accessed_bin = sqlite3.Binary(pickle.dumps(last_accessed)) + + sql_command = ( + "UPDATE books SET Position = ?, LastAccessed = ? WHERE Hash = ?") - sql_command = "UPDATE books SET Position = ? WHERE Hash = ?" try: self.database.execute( sql_command, - [sqlite3.Binary(pickled_position), file_hash]) + [position_bin, last_accessed_bin, file_hash]) except sqlite3.OperationalError: print('SQLite is in rebellion, Commander') return diff --git a/library.py b/library.py index 3eb4a60..39081bb 100644 --- a/library.py +++ b/library.py @@ -42,7 +42,7 @@ class Library: books = database.DatabaseFunctions( self.parent.database_path).fetch_data( ('Title', 'Author', 'Year', 'DateAdded', 'Path', - 'Position', 'ISBN', 'Tags', 'Hash',), + 'Position', 'ISBN', 'Tags', 'Hash', 'LastAccessed'), 'books', {'Title': ''}, 'LIKE') @@ -65,7 +65,7 @@ class Library: books.append([ i[1]['title'], i[1]['author'], i[1]['year'], current_qdatetime, - i[1]['path'], None, i[1]['isbn'], _tags, i[0]]) + i[1]['path'], None, i[1]['isbn'], _tags, i[0], None]) else: return @@ -73,12 +73,12 @@ class Library: for i in books: # The database query returns (or the extension data is) # an iterable with the following indices: - # Index 0 is the key ID is ignored title = i[0] author = i[1] year = i[2] path = i[4] tags = i[7] + last_accessed = i[9] try: date_added = pickle.loads(i[3]) @@ -101,6 +101,7 @@ class Library: 'isbn': i[6], 'tags': tags, 'hash': i[8], + 'last_accessed': last_accessed, 'file_exists': file_exists} tooltip_string = title + '\nAuthor: ' + author + '\nYear: ' + str(year) @@ -129,6 +130,7 @@ class Library: item.setData(position, QtCore.Qt.UserRole + 7) item.setData(False, QtCore.Qt.UserRole + 8) # Is the cover being displayed? item.setData(date_added, QtCore.Qt.UserRole + 9) + item.setData(last_accessed, QtCore.Qt.UserRole + 12) item.setIcon(QtGui.QIcon(img_pixmap)) self.view_model.appendRow(item) @@ -182,7 +184,8 @@ class Library: self.proxy_model.invalidateFilter() self.proxy_model.setFilterParams( self.parent.libraryToolBar.searchBar.text(), - self.parent.active_library_filters) + self.parent.active_library_filters, + self.parent.libraryToolBar.sortingBox.currentIndex()) self.proxy_model.setFilterFixedString( self.parent.libraryToolBar.searchBar.text()) @@ -200,7 +203,8 @@ class Library: 0: 0, 1: 1, 2: 2, - 3: 9} + 3: 9, + 4: 12} # Sorting according to roles and the drop down in the library toolbar self.proxy_model.setSortRole( @@ -208,7 +212,7 @@ class Library: # This can be expanded to other fields by appending to the list sort_order = QtCore.Qt.AscendingOrder - if self.parent.libraryToolBar.sortingBox.currentIndex() in [3]: + if self.parent.libraryToolBar.sortingBox.currentIndex() in [3, 4]: sort_order = QtCore.Qt.DescendingOrder self.proxy_model.sort(0, sort_order) diff --git a/models.py b/models.py index f387675..c3a7801 100644 --- a/models.py +++ b/models.py @@ -27,10 +27,12 @@ class ItemProxyModel(QtCore.QSortFilterProxyModel): super(ItemProxyModel, self).__init__(parent) self.filter_text = None self.active_library_filters = None + self.sorting_position = None - def setFilterParams(self, filter_text, active_library_filters): + def setFilterParams(self, filter_text, active_library_filters, sorting_position): self.filter_text = filter_text self.active_library_filters = [i.lower() for i in active_library_filters] + self.sorting_position = sorting_position def filterAcceptsRow(self, row, parent): model = self.sourceModel() @@ -42,6 +44,10 @@ class ItemProxyModel(QtCore.QSortFilterProxyModel): tags = model.data(this_index, QtCore.Qt.UserRole + 4) directory_name = model.data(this_index, QtCore.Qt.UserRole + 10) 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: + return False if self.active_library_filters: if directory_name not in self.active_library_filters: diff --git a/threaded.py b/threaded.py index 270aa50..b6c1039 100644 --- a/threaded.py +++ b/threaded.py @@ -36,7 +36,9 @@ class BackGroundTabUpdate(QtCore.QThread): for i in self.all_metadata: file_hash = i['hash'] position = i['position'] - hash_position_pairs.append([file_hash, position]) + last_accessed = i['last_accessed'] + + hash_position_pairs.append([file_hash, position, last_accessed]) database.DatabaseFunctions( self.database_path).modify_position(hash_position_pairs) diff --git a/widgets.py b/widgets.py index 41df0b1..3baf294 100644 --- a/widgets.py +++ b/widgets.py @@ -333,7 +333,7 @@ class LibraryToolBar(QtWidgets.QToolBar): self.searchBar.setObjectName('searchBar') # Sorter - sorting_choices = ['Title', 'Author', 'Year', 'Newest'] + sorting_choices = ['Title', 'Author', 'Year', 'Newest', 'Last read'] self.sortingBox = FixedComboBox(self) self.sortingBox.addItems(sorting_choices) self.sortingBox.setObjectName('sortingBox') @@ -374,10 +374,6 @@ class FixedPushButton(QtWidgets.QPushButton): class Tab(QtWidgets.QWidget): def __init__(self, metadata, parent=None): - # TODO - # Take hint from a position function argument to open the book - # at a specific page - super(Tab, self).__init__(parent) self.parent = parent self.metadata = metadata # Save progress data into this dictionary @@ -387,10 +383,10 @@ class Tab(QtWidgets.QWidget): self.horzLayout.setOrientation(QtCore.Qt.Horizontal) self.masterLayout.addWidget(self.horzLayout) + self.metadata['last_accessed'] = QtCore.QDateTime().currentDateTime() + position = self.metadata['position'] - # TODO - # Chapter position and vertical scrollbar position if position: current_chapter = position['current_chapter'] else: @@ -461,6 +457,19 @@ class Tab(QtWidgets.QWidget): self.contentView.setFocus() + def update_last_accessed_time(self): + self.metadata['last_accessed'] = QtCore.QDateTime().currentDateTime() + + start_index = self.window().lib_ref.view_model.index(0, 0) + matching_item = self.window().lib_ref.view_model.match( + start_index, + QtCore.Qt.UserRole + 6, + self.metadata['hash'], + 1, QtCore.Qt.MatchExactly) + + 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): if switch_widgets: previous_widget = self.window().tabWidget.currentWidget() @@ -489,7 +498,7 @@ class Tab(QtWidgets.QWidget): def generate_position(self): total_chapters = len(self.metadata['content'].keys()) # TODO - # Calculate lines + # Calculate lines to incorporate into progress self.metadata['position'] = { 'current_chapter': 1, 'current_line': 0, @@ -754,9 +763,13 @@ class PliantWidgetsCommonFunctions(): self.main_window = main_window def wheelEvent(self, event, are_we_doing_images_only): + ignore_events = 20 + if are_we_doing_images_only: + ignore_events = 10 + if self.pw.ignore_wheel_event: self.pw.ignore_wheel_event_number += 1 - if self.pw.ignore_wheel_event_number > 20: + if self.pw.ignore_wheel_event_number > ignore_events: self.pw.ignore_wheel_event = False self.pw.ignore_wheel_event_number = 0 return