Table multicolumn search, split models into new module
This commit is contained in:
@@ -136,6 +136,7 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
|
|||||||
self.libraryToolBar.tableViewButton.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.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_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.sortingBox.activated.connect(self.lib_ref.update_proxymodel)
|
||||||
self.addToolBar(self.libraryToolBar)
|
self.addToolBar(self.libraryToolBar)
|
||||||
|
|
||||||
@@ -192,7 +193,7 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
|
|||||||
|
|
||||||
# Init display models
|
# Init display models
|
||||||
self.lib_ref.generate_model('build')
|
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()
|
self.lib_ref.create_proxymodel()
|
||||||
|
|
||||||
# ListView
|
# ListView
|
||||||
@@ -285,6 +286,12 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
|
|||||||
self.sorterProgress.setVisible(False)
|
self.sorterProgress.setVisible(False)
|
||||||
self.lib_ref.create_proxymodel()
|
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):
|
def delete_books(self):
|
||||||
selected_books = self.listView.selectedIndexes()
|
selected_books = self.listView.selectedIndexes()
|
||||||
if selected_books:
|
if selected_books:
|
||||||
|
26
library.py
26
library.py
@@ -5,7 +5,7 @@ import pickle
|
|||||||
import database
|
import database
|
||||||
|
|
||||||
from PyQt5 import QtWidgets, QtGui, QtCore
|
from PyQt5 import QtWidgets, QtGui, QtCore
|
||||||
from widgets import LibraryItemModel, LibraryTableModel
|
from models import LibraryItemModel, LibraryTableModel, TableProxyModel
|
||||||
|
|
||||||
|
|
||||||
class Library:
|
class Library:
|
||||||
@@ -22,6 +22,7 @@ class Library:
|
|||||||
# because I kinda sorta NEED the match() method
|
# because I kinda sorta NEED the match() method
|
||||||
|
|
||||||
if mode == 'build':
|
if mode == 'build':
|
||||||
|
self.table_rows = []
|
||||||
self.view_model = LibraryItemModel()
|
self.view_model = LibraryItemModel()
|
||||||
|
|
||||||
books = database.DatabaseFunctions(
|
books = database.DatabaseFunctions(
|
||||||
@@ -126,11 +127,28 @@ class Library:
|
|||||||
self.table_rows.append(
|
self.table_rows.append(
|
||||||
(title, author, year, tags, path))
|
(title, author, year, tags, path))
|
||||||
|
|
||||||
def create_tablemodel(self):
|
def create_table_model(self):
|
||||||
table_header = ['Title', 'Author', 'Year', 'Tags']
|
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.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):
|
def create_proxymodel(self):
|
||||||
self.proxy_model = QtCore.QSortFilterProxyModel()
|
self.proxy_model = QtCore.QSortFilterProxyModel()
|
||||||
|
76
models.py
Normal file
76
models.py
Normal file
@@ -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
|
48
widgets.py
48
widgets.py
@@ -723,51 +723,3 @@ class LibraryDelegate(QtWidgets.QStyledItemDelegate):
|
|||||||
y_draw = option.rect.bottomRight().y() - 35
|
y_draw = option.rect.bottomRight().y() - 35
|
||||||
if current_chapter != 1:
|
if current_chapter != 1:
|
||||||
painter.drawPixmap(x_draw, y_draw, read_icon)
|
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()"))
|
|
||||||
|
Reference in New Issue
Block a user