From 7aa9b9daff800f9c45af92d8787420433e3da80b Mon Sep 17 00:00:00 2001 From: BasioMeusPuga Date: Tue, 28 Nov 2017 11:09:04 +0530 Subject: [PATCH] Table multicolumn search, split models into new module --- __main__.py | 9 ++++++- library.py | 26 +++++++++++++++--- models.py | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++ widgets.py | 48 --------------------------------- 4 files changed, 106 insertions(+), 53 deletions(-) create mode 100644 models.py diff --git a/__main__.py b/__main__.py index 2642051..413ea77 100755 --- a/__main__.py +++ b/__main__.py @@ -136,6 +136,7 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow): 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.addToolBar(self.libraryToolBar) @@ -192,7 +193,7 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow): # Init display models self.lib_ref.generate_model('build') - self.lib_ref.create_tablemodel() # TODO - Make this accompany other proxy model generations + self.lib_ref.create_table_model() # TODO - Make this accompany other proxy model generations self.lib_ref.create_proxymodel() # ListView @@ -285,6 +286,12 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow): self.sorterProgress.setVisible(False) self.lib_ref.create_proxymodel() + # Create the table model + # Since images aren't displayed here, it's fast enough to not + # need addition to + # self.create_table_model() + self.lib_ref.create_table_model() + def delete_books(self): selected_books = self.listView.selectedIndexes() if selected_books: diff --git a/library.py b/library.py index cafee02..fa49a7c 100644 --- a/library.py +++ b/library.py @@ -5,7 +5,7 @@ import pickle import database from PyQt5 import QtWidgets, QtGui, QtCore -from widgets import LibraryItemModel, LibraryTableModel +from models import LibraryItemModel, LibraryTableModel, TableProxyModel class Library: @@ -22,6 +22,7 @@ class Library: # because I kinda sorta NEED the match() method if mode == 'build': + self.table_rows = [] self.view_model = LibraryItemModel() books = database.DatabaseFunctions( @@ -126,11 +127,28 @@ class Library: self.table_rows.append( (title, author, year, tags, path)) - def create_tablemodel(self): + def create_table_model(self): table_header = ['Title', 'Author', 'Year', 'Tags'] - self.table_rows.sort(key=lambda x: x[0]) + # self.table_rows.sort(key=lambda x: x[0]) self.table_model = LibraryTableModel(table_header, self.table_rows) - self.parent_window.tableView.setModel(self.table_model) + self.create_table_proxy_model() + + def create_table_proxy_model(self): + self.table_proxy_model = TableProxyModel() + self.table_proxy_model.setSourceModel(self.table_model) + self.parent_window.tableView.setModel(self.table_proxy_model) + + def update_table_proxy_model(self): + self.table_proxy_model.invalidateFilter() + self.table_proxy_model.setFilterParams( + self.parent_window.libraryToolBar.searchBar.text(), [0, 1, 3]) + + # 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. + self.table_proxy_model.setFilterFixedString( + self.parent_window.libraryToolBar.searchBar.text()) def create_proxymodel(self): self.proxy_model = QtCore.QSortFilterProxyModel() diff --git a/models.py b/models.py new file mode 100644 index 0000000..2084886 --- /dev/null +++ b/models.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python3 + +from PyQt5 import QtCore, QtGui + + +class LibraryItemModel(QtGui.QStandardItemModel, QtCore.QAbstractItemModel): + def __init__(self, parent=None): + # We're using this to be able to access the match() method + super(LibraryItemModel, self).__init__(parent) + + +class LibraryTableModel(QtCore.QAbstractTableModel): + # TODO + # Speed up sorting + # Double clicking + # Auto resize with emphasis on Name + + def __init__(self, header_data, display_data, parent=None): + super(LibraryTableModel, self).__init__(parent) + self.header_data = header_data + self.display_data = display_data + + def rowCount(self, parent): + return len(self.display_data) + + def columnCount(self, parent): + return len(self.header_data) + + def data(self, index, role): + if not index.isValid(): + return None + + if role == QtCore.Qt.DisplayRole: + 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 sort(self, col, order): + # self.emit(SIGNAL("layoutAboutToBeChanged()")) + self.display_data.sort(key=lambda x: x[col]) + if order == QtCore.Qt.DescendingOrder: + self.display_data.sort(key=lambda x: x[col], reverse=True) + + # self.emit(SIGNAL("layoutChanged()")) + + +class TableProxyModel(QtCore.QSortFilterProxyModel): + def __init__(self, parent=None): + super(TableProxyModel, self).__init__(parent) + self.filter_string = None + self.filter_columns = None + + def setFilterParams(self, filter_text, filter_columns): + self.filter_string = filter_text.lower() + self.filter_columns = filter_columns + + 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] + + for i in valid_data: + if self.filter_string in i: + return True + + return False diff --git a/widgets.py b/widgets.py index 1789b27..b248abe 100644 --- a/widgets.py +++ b/widgets.py @@ -723,51 +723,3 @@ class LibraryDelegate(QtWidgets.QStyledItemDelegate): y_draw = option.rect.bottomRight().y() - 35 if current_chapter != 1: painter.drawPixmap(x_draw, y_draw, read_icon) - - -class LibraryItemModel(QtGui.QStandardItemModel, QtCore.QAbstractItemModel): - def __init__(self, parent=None): - # We're using this to be able to access the match() method - super(LibraryItemModel, self).__init__(parent) - -class LibraryTableModel(QtCore.QAbstractTableModel): - # TODO - # Speed up sorting - # Associate with a proxy model to enable searching - # Double clicking - # Auto resize with emphasis on Name - # Hide path but send it anyway - - def __init__(self, header_data, display_data, parent=None): - super(LibraryTableModel, self).__init__(parent) - self.header_data = header_data - self.display_data = display_data - - def rowCount(self, parent): - return len(self.display_data) - - def columnCount(self, parent): - return len(self.header_data) - - def data(self, index, role): - if not index.isValid(): - return None - - if role == QtCore.Qt.DisplayRole: - 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 sort(self, col, order): - # self.emit(SIGNAL("layoutAboutToBeChanged()")) - self.display_data.sort(key=lambda x: x[col]) - if order == QtCore.Qt.DescendingOrder: - self.display_data.sort(key=lambda x: x[col], reverse=True) - - # self.emit(SIGNAL("layoutChanged()"))