From cc19d5f1444f3398059888e964db975ea058b9a8 Mon Sep 17 00:00:00 2001 From: BasioMeusPuga Date: Thu, 1 Mar 2018 17:25:28 +0530 Subject: [PATCH] Remove Table Model specific code --- TODO | 1 + __main__.py | 51 ++++++------------- library.py | 86 ++++++++++++-------------------- models.py | 139 ---------------------------------------------------- 4 files changed, 47 insertions(+), 230 deletions(-) diff --git a/TODO b/TODO index 0b6c1ce..8de720f 100644 --- a/TODO +++ b/TODO @@ -21,6 +21,7 @@ TODO ✓ Mass tagging ✓ Add capability to sort by new Table view + Get sorting working again Ignore a / the / numbers for sorting purposes Information dialog widget Context menu: Cache, Read, Edit database, delete, Mark read/unread diff --git a/__main__.py b/__main__.py index f794603..72230b5 100755 --- a/__main__.py +++ b/__main__.py @@ -116,9 +116,8 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow): self.libraryToolBar.coverViewButton.triggered.connect(self.switch_library_view) self.libraryToolBar.tableViewButton.triggered.connect(self.switch_library_view) self.libraryToolBar.settingsButton.triggered.connect(self.show_settings) - self.libraryToolBar.searchBar.textChanged.connect(self.lib_ref.update_proxymodel) - self.libraryToolBar.searchBar.textChanged.connect(self.lib_ref.update_table_proxy_model) - self.libraryToolBar.sortingBox.activated.connect(self.lib_ref.update_proxymodel) + self.libraryToolBar.searchBar.textChanged.connect(self.lib_ref.update_proxymodels) + self.libraryToolBar.sortingBox.activated.connect(self.lib_ref.update_proxymodels) self.libraryToolBar.libraryFilterButton.setPopupMode(QtWidgets.QToolButton.InstantPopup) self.addToolBar(self.libraryToolBar) @@ -194,8 +193,7 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow): # Init display models self.lib_ref.generate_model('build') - self.lib_ref.create_table_model() - self.lib_ref.create_proxymodel() + self.lib_ref.generate_proxymodels() self.lib_ref.generate_library_tags() self.set_library_filter() self.start_culling_timer() @@ -212,7 +210,7 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow): self.tableView.doubleClicked.connect(self.library_doubleclick) self.tableView.horizontalHeader().setSectionResizeMode( QtWidgets.QHeaderView.Interactive) - self.tableView.horizontalHeader().setSortIndicator(1, QtCore.Qt.AscendingOrder) + self.tableView.horizontalHeader().setSortIndicator(2, QtCore.Qt.AscendingOrder) self.tableView.setColumnHidden(0, True) self.tableView.horizontalHeader().setHighlightSections(False) if self.settings['main_window_headers']: @@ -275,8 +273,8 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow): blank_pixmap.load(':/images/blank.png') all_indexes = set() - for i in range(self.lib_ref.proxy_model.rowCount()): - all_indexes.add(self.lib_ref.proxy_model.index(i, 0)) + for i in range(self.lib_ref.item_proxy_model.rowCount()): + all_indexes.add(self.lib_ref.item_proxy_model.index(i, 0)) y_range = list(range(0, self.listView.viewport().height(), 100)) y_range.extend((-20, self.listView.viewport().height() + 20)) @@ -290,7 +288,7 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow): invisible_indexes = all_indexes - visible_indexes for i in invisible_indexes: - model_index = self.lib_ref.proxy_model.mapToSource(i) + model_index = self.lib_ref.item_proxy_model.mapToSource(i) this_item = self.lib_ref.view_model.item(model_index.row()) if this_item: @@ -298,7 +296,7 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow): this_item.setData(False, QtCore.Qt.UserRole + 8) for i in visible_indexes: - model_index = self.lib_ref.proxy_model.mapToSource(i) + model_index = self.lib_ref.item_proxy_model.mapToSource(i) this_item = self.lib_ref.view_model.item(model_index.row()) if this_item: @@ -429,7 +427,7 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow): # Get a list of QItemSelection objects # What we're interested in is the indexes()[0] in each of them # That gives a list of indexes from the view model - selected_books = self.lib_ref.proxy_model.mapSelectionToSource( + selected_books = self.lib_ref.item_proxy_model.mapSelectionToSource( self.listView.selectionModel().selection()) if not selected_books: @@ -484,8 +482,7 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow): self.sorterProgress.setVisible(False) self.sorterProgress.setValue(0) - self.lib_ref.create_table_model() - self.lib_ref.create_proxymodel() + self.lib_ref.generate_proxymodels() self.lib_ref.generate_library_tags() if not self.settings['perform_culling']: @@ -519,11 +516,11 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow): self.bookToolBar.hide() self.libraryToolBar.show() - if self.lib_ref.proxy_model: + if self.lib_ref.item_proxy_model: # Making the proxy model available doesn't affect # memory utilization at all. Bleh. self.statusMessage.setText( - str(self.lib_ref.proxy_model.rowCount()) + ' Books') + str(self.lib_ref.item_proxy_model.rowCount()) + ' Books') else: if self.settings['show_toolbars']: @@ -575,12 +572,9 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow): def set_toc_position(self, event=None): current_tab = self.tabWidget.widget(self.tabWidget.currentIndex()) - # We're updating the underlying models to have real-time + # We're updating the underlying model to have real-time # updates on the read status - # Since there are 2 separate models, they will each have to - # be updated individually - # The listView model # Set a baseline model index in case the item gets deleted # E.g It's open in a tab and deleted from the library model_index = None @@ -602,20 +596,6 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow): self.lib_ref.view_model.setData( model_index, current_tab.metadata['position'], QtCore.Qt.UserRole + 7) - # The tableView model - model_index = None - start_index = self.lib_ref.table_model.index(0, 0) - matching_item = self.lib_ref.table_model.match( - start_index, - QtCore.Qt.UserRole + 1, - current_tab.metadata['hash'], - 1, QtCore.Qt.MatchExactly) - - if matching_item: - model_row = matching_item[0].row() - self.lib_ref.table_model.display_data[model_row][5][ - 'position'] = current_tab.metadata['position'] - # Go on to change the value of the Table of Contents box current_tab.change_chapter_tocBox() self.format_contentView() @@ -641,7 +621,7 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow): sender = self.sender().objectName() if sender == 'listView': - source_index = self.lib_ref.proxy_model.mapToSource(index) + source_index = self.lib_ref.item_proxy_model.mapToSource(index) elif sender == 'tableView': source_index = self.lib_ref.table_proxy_model.mapToSource(index) @@ -973,8 +953,7 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow): else: self.library_filter_menu.actions()[-1].setChecked(True) - self.lib_ref.update_proxymodel() - self.lib_ref.update_table_proxy_model() + self.lib_ref.update_proxymodels() def toggle_toolbars(self): self.settings['show_toolbars'] = not self.settings['show_toolbars'] diff --git a/library.py b/library.py index 053aed0..9d10a7f 100644 --- a/library.py +++ b/library.py @@ -22,21 +22,18 @@ import pathlib from PyQt5 import QtGui, QtCore import database -from models import MostExcellentTableModel, TableProxyModel, ItemProxyModel +from models import TableProxyModel, ItemProxyModel class Library: def __init__(self, parent): self.parent = parent self.view_model = None - self.proxy_model = None - self.table_model = None + self.item_proxy_model = None self.table_proxy_model = None - self.table_rows = [] def generate_model(self, mode, parsed_books=None, is_database_ready=True): if mode == 'build': - self.table_rows = [] self.view_model = QtGui.QStandardItemModel() self.view_model.setColumnCount(10) @@ -142,13 +139,14 @@ class Library: if not self.parent.settings['perform_culling'] and is_database_ready: self.parent.load_all_covers() - def create_table_model(self): - table_header = ['Title', 'Author', 'Status', 'Year', 'Tags'] - self.table_model = MostExcellentTableModel( - table_header, self.table_rows, self.parent.temp_dir.path()) - self.create_table_proxy_model() + def generate_proxymodels(self): + self.item_proxy_model = ItemProxyModel() + self.item_proxy_model.setSourceModel(self.view_model) + self.item_proxy_model.setSortCaseSensitivity(False) + s = QtCore.QSize(160, 250) # Set icon sizing here + self.parent.listView.setIconSize(s) + self.parent.listView.setModel(self.item_proxy_model) - def create_table_proxy_model(self): self.table_proxy_model = TableProxyModel(self.parent.temp_dir.path()) self.table_proxy_model.setSourceModel(self.view_model) self.table_proxy_model.setSortCaseSensitivity(False) @@ -156,39 +154,32 @@ class Library: self.parent.tableView.setModel(self.table_proxy_model) self.parent.tableView.horizontalHeader().setSortIndicator( 0, QtCore.Qt.AscendingOrder) - self.update_table_proxy_model() - def update_table_proxy_model(self): + self.update_proxymodels() + + def update_proxymodels(self): + # Table proxy model self.table_proxy_model.invalidateFilter() self.table_proxy_model.setFilterParams( self.parent.libraryToolBar.searchBar.text(), self.parent.active_library_filters, - self.parent.libraryToolBar.sortingBox.currentIndex()) - # This isn't needed, but it forces a model update every time the - # text in the line edit changes. So I guess it is needed. + 0) # This doesn't need to know the sorting box position self.table_proxy_model.setFilterFixedString( self.parent.libraryToolBar.searchBar.text()) + # ^^^ This isn't needed, but it forces a model update every time the + # text in the line edit changes. So I guess it is needed. - def create_proxymodel(self): - self.proxy_model = ItemProxyModel() - self.proxy_model.setSourceModel(self.view_model) - self.proxy_model.setSortCaseSensitivity(False) - s = QtCore.QSize(160, 250) # Set icon sizing here - self.parent.listView.setIconSize(s) - self.parent.listView.setModel(self.proxy_model) - self.update_proxymodel() - - def update_proxymodel(self): - self.proxy_model.invalidateFilter() - self.proxy_model.setFilterParams( + # Item proxy model + self.item_proxy_model.invalidateFilter() + self.item_proxy_model.setFilterParams( self.parent.libraryToolBar.searchBar.text(), self.parent.active_library_filters, self.parent.libraryToolBar.sortingBox.currentIndex()) - self.proxy_model.setFilterFixedString( + self.item_proxy_model.setFilterFixedString( self.parent.libraryToolBar.searchBar.text()) self.parent.statusMessage.setText( - str(self.proxy_model.rowCount()) + ' books') + str(self.item_proxy_model.rowCount()) + ' books') # TODO # Allow sorting by type @@ -205,7 +196,7 @@ class Library: 4: 12} # Sorting according to roles and the drop down in the library toolbar - self.proxy_model.setSortRole( + self.item_proxy_model.setSortRole( QtCore.Qt.UserRole + sort_roles[self.parent.libraryToolBar.sortingBox.currentIndex()]) # This can be expanded to other fields by appending to the list @@ -213,7 +204,7 @@ class Library: if self.parent.libraryToolBar.sortingBox.currentIndex() in [3, 4]: sort_order = QtCore.Qt.DescendingOrder - self.proxy_model.sort(0, sort_order) + self.item_proxy_model.sort(0, sort_order) self.parent.start_culling_timer() def generate_library_tags(self): @@ -250,8 +241,7 @@ class Library: return 'manually added', None - # Both the models will have to be done separately - # Item Model + # Generate tags for the QStandardItemModel for i in range(self.view_model.rowCount()): this_item = self.view_model.item(i, 0) all_metadata = this_item.data(QtCore.Qt.UserRole + 3) @@ -260,19 +250,6 @@ class Library: this_item.setData(directory_name, QtCore.Qt.UserRole + 10) this_item.setData(directory_tags, QtCore.Qt.UserRole + 11) - # Table Model - for count, i in enumerate(self.table_model.display_data): - all_metadata = i[5] - - directory_name, directory_tags = get_tags(all_metadata) - try: - i[7] = directory_name - i[8] = directory_tags - except IndexError: - i.extend([directory_name, directory_tags]) - - self.table_model.display_data[count] = i - def prune_models(self, valid_paths): # To be executed when the library is updated by folder # All files in unselected directories will have to be removed @@ -280,17 +257,16 @@ class Library: # They will also have to be deleted from the library valid_paths = set(valid_paths) - # Get all paths in the dictionary from either of the models - # self.table_rows has all file metadata in position 5 - all_paths = [i[5]['path'] for i in self.table_rows] - all_paths = set(all_paths) + # Get all paths + all_paths = set() + for i in range(self.view_model.rowCount()): + item = self.view_model.item(i, 0) + item_metadata = item.data(QtCore.Qt.UserRole + 3) + book_path = item_metadata['path'] + all_paths.add(book_path) invalid_paths = all_paths - valid_paths - # Remove invalid paths from both of the models - self.table_rows = [ - i for i in self.table_rows if i[5]['path'] not in invalid_paths] - deletable_persistent_indexes = [] for i in range(self.view_model.rowCount()): item = self.view_model.item(i) diff --git a/models.py b/models.py index f2c5a77..63330da 100644 --- a/models.py +++ b/models.py @@ -184,145 +184,6 @@ class ProxyModelsCommonFunctions: return False -class MostExcellentTableModel(QtCore.QAbstractTableModel): - # Sorting is taken care of by the QSortFilterProxy model - # which has an inbuilt sort method - - # Modifying data in the table model is a case of modifying the - # data sent to it as a list - # In this case, that's self.data_list - - def __init__(self, header_data, display_data, temp_dir=None, parent=None): - super(MostExcellentTableModel, self).__init__(parent) - self.header_data = header_data - self.display_data = display_data - self.temp_dir = temp_dir - - def rowCount(self, parent): - if self.display_data: - return len(self.display_data) - else: - return 0 - - def columnCount(self, parent): - return len(self.header_data) - - def data(self, index, role): - if not index.isValid(): - return None - - # This block specializes this function for the library - # Not having a self.temp_dir allows for its reuse elsewhere - if self.temp_dir: - if role == QtCore.Qt.DecorationRole and index.column() == 2: - return_pixmap = None - file_exists = self.display_data[index.row()][5]['file_exists'] - position = self.display_data[index.row()][5]['position'] - - if not file_exists: - return_pixmap = pie_chart.pixmapper( - -1, None, None, QtCore.Qt.SizeHintRole + 10) - - if position: - current_chapter = position['current_chapter'] - total_chapters = position['total_chapters'] - - return_pixmap = pie_chart.pixmapper( - current_chapter, total_chapters, self.temp_dir, - QtCore.Qt.SizeHintRole + 10) - - return return_pixmap - - # The rest of the roles can be accomodated here. - elif role == QtCore.Qt.UserRole: - value = self.display_data[index.row()][5] # File metadata - return value - - elif role == QtCore.Qt.UserRole + 1: - value = self.display_data[index.row()][6] # File hash - return value - - #_________________________________ - # The EditRole is so that editing a cell doesn't clear its contents - if role == QtCore.Qt.DisplayRole or role == QtCore.Qt.EditRole: - value = self.display_data[index.row()][index.column()] - return value - - else: - return QtCore.QVariant() - - def headerData(self, col, orientation, role): - if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole: - return self.header_data[col] - return None - - def flags(self, index): - # This means only the Tags column is editable - if self.temp_dir and index.column() == 4: - return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEditable - else: - # These are standard select but don't edit values - return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable - - def setData(self, index, value, role=QtCore.Qt.EditRole): - # We don't need to connect this to dataChanged since the underlying - # table model (not the proxy model) is the one that's being updated - - # Database tags for files should not be updated each time - # a new folder gets added or deleted from the directory - # This will be done @ runtime - # Individually set file tags will be preserved - # Duplicate file tags will be removed - - row = index.row() - col = index.column() - self.display_data[row][col] = value - return True - - -class TableProxyModel2(QtCore.QSortFilterProxyModel): - def __init__(self, parent=None): - super(TableProxyModel2, self).__init__(parent) - self.filter_string = None - self.filter_columns = None - self.active_library_filters = None - - def setFilterParams(self, filter_text, filter_columns, active_library_filters): - self.filter_string = filter_text.lower() - self.filter_columns = filter_columns - self.active_library_filters = [i.lower() for i in active_library_filters] - - def filterAcceptsRow(self, row_num, parent): - if self.filter_string is None or self.filter_columns is None: - return True - - model = self.sourceModel() - - valid_indices = [model.index(row_num, i) for i in self.filter_columns] - valid_data = [ - model.data(i, QtCore.Qt.DisplayRole).lower() for i in valid_indices if model.data( - i, QtCore.Qt.DisplayRole) is not None] - - try: - valid_data.extend([model.display_data[row_num][7], model.display_data[row_num][8]]) - except IndexError: # Columns 7 and 8 are added after creation of the model - pass - - # Filter out all books not in the active library filters - if self.active_library_filters: - current_library_name = valid_data[-2].lower() - if current_library_name not in self.active_library_filters: - return False - else: - return False - - for i in valid_data: - if i: - if self.filter_string in i: - return True - return False - - class MostExcellentFileSystemModel(QtWidgets.QFileSystemModel): # Directories are tracked on the basis of their paths # Poll the tag_data dictionary to get User selection