带有日期和 sqlite 的 QSortFilterProxyModel
QSortFilterProxyModel with dates and sqlite
我几乎一字不差地在 PyQt5 上重新实现了这个例子。这个例子中的问题是:它假设你在模型中输入一个 QDate,但在我的例子中,模型是从一个 sqlite 数据库中获取的,因此日期只是文本。
from PyQt5.QtCore import (QDate, QDateTime, QRegExp, QSortFilterProxyModel, Qt,
QTime)
from PyQt5.QtGui import QStandardItemModel
from PyQt5.QtWidgets import (QApplication, QCheckBox, QComboBox, QDateEdit,
QGridLayout, QGroupBox, QHBoxLayout, QLabel, QLineEdit, QTreeView,
QVBoxLayout, QWidget,QTableView)
class MySortFilterProxyModel(QSortFilterProxyModel):
def __init__(self, parent=None):
super(MySortFilterProxyModel, self).__init__(parent)
self.minDate = QDate()
self.maxDate = QDate()
def setFilterMinimumDate(self, date):
self.minDate = date
self.invalidateFilter()
def filterMinimumDate(self):
return self.minDate
def setFilterMaximumDate(self, date):
self.maxDate = date
self.invalidateFilter()
def filterMaximumDate(self):
return self.maxDate
def filterAcceptsRow(self, sourceRow, sourceParent):
index0 = self.sourceModel().index(sourceRow, 0, sourceParent)
index1 = self.sourceModel().index(sourceRow, 1, sourceParent)
index2 = self.sourceModel().index(sourceRow, 2, sourceParent)
return ((self.filterRegExp().indexIn(self.sourceModel().data(index0)) >= 0
or self.filterRegExp().indexIn(self.sourceModel().data(index1)) >= 0)
and self.dateInRange(self.sourceModel().data(index2)))
def dateInRange(self, date):
if isinstance(date, QDateTime):
date = date.date()
return ((not self.minDate.isValid() or date >= self.minDate)
and (not self.maxDate.isValid() or date <= self.maxDate))
class Window(QWidget):
def __init__(self):
super(Window, self).__init__()
self.proxyModel = MySortFilterProxyModel(self)
self.proxyModel.setDynamicSortFilter(True)
self.sourceView = QTreeView()
self.sourceView.setRootIsDecorated(False)
self.sourceView.setAlternatingRowColors(True)
sourceLayout = QHBoxLayout()
sourceLayout.addWidget(self.sourceView)
sourceGroupBox = QGroupBox("Original Model")
sourceGroupBox.setLayout(sourceLayout)
self.filterCaseSensitivityCheckBox = QCheckBox("Case sensitive filter")
self.filterCaseSensitivityCheckBox.setChecked(True)
self.filterPatternLineEdit = QLineEdit()
self.filterPatternLineEdit.setText("Grace|Sports")
filterPatternLabel = QLabel("&Filter pattern:")
filterPatternLabel.setBuddy(self.filterPatternLineEdit)
self.filterSyntaxComboBox = QComboBox()
self.filterSyntaxComboBox.addItem("Regular expression", QRegExp.RegExp)
self.filterSyntaxComboBox.addItem("Wildcard", QRegExp.Wildcard)
self.filterSyntaxComboBox.addItem("Fixed string", QRegExp.FixedString)
self.fromDateEdit = QDateEdit()
self.fromDateEdit.setDate(QDate(2006, 12, 22))
self.fromDateEdit.setCalendarPopup(True)
fromLabel = QLabel("F&rom:")
fromLabel.setBuddy(self.fromDateEdit)
self.toDateEdit = QDateEdit()
self.toDateEdit.setDate(QDate(2007, 1, 5))
self.toDateEdit.setCalendarPopup(True)
toLabel = QLabel("&To:")
toLabel.setBuddy(self.toDateEdit)
self.filterPatternLineEdit.textChanged.connect(self.textFilterChanged)
self.filterSyntaxComboBox.currentIndexChanged.connect(self.textFilterChanged)
self.filterCaseSensitivityCheckBox.toggled.connect(self.textFilterChanged)
self.fromDateEdit.dateChanged.connect(self.dateFilterChanged)
self.toDateEdit.dateChanged.connect(self.dateFilterChanged)
self.proxyView = QTableView()
# self.proxyView.setRootIsDecorated(False)
self.proxyView.setAlternatingRowColors(True)
self.proxyView.setModel(self.proxyModel)
self.proxyView.setSortingEnabled(True)
self.proxyView.sortByColumn(1, Qt.AscendingOrder)
self.textFilterChanged()
self.dateFilterChanged()
proxyLayout = QGridLayout()
proxyLayout.addWidget(self.proxyView, 0, 0, 1, 3)
proxyLayout.addWidget(filterPatternLabel, 1, 0)
proxyLayout.addWidget(self.filterPatternLineEdit, 1, 1)
proxyLayout.addWidget(self.filterSyntaxComboBox, 1, 2)
proxyLayout.addWidget(self.filterCaseSensitivityCheckBox, 2, 0, 1, 3)
proxyLayout.addWidget(fromLabel, 3, 0)
proxyLayout.addWidget(self.fromDateEdit, 3, 1, 1, 2)
proxyLayout.addWidget(toLabel, 4, 0)
proxyLayout.addWidget(self.toDateEdit, 4, 1, 1, 2)
proxyGroupBox = QGroupBox("Sorted/Filtered Model")
proxyGroupBox.setLayout(proxyLayout)
mainLayout = QVBoxLayout()
mainLayout.addWidget(sourceGroupBox)
mainLayout.addWidget(proxyGroupBox)
self.setLayout(mainLayout)
self.setWindowTitle("Custom Sort/Filter Model")
self.resize(500, 450)
def setSourceModel(self, model):
self.proxyModel.setSourceModel(model)
self.sourceView.setModel(model)
def textFilterChanged(self):
syntax = QRegExp.PatternSyntax(
self.filterSyntaxComboBox.itemData(
self.filterSyntaxComboBox.currentIndex()))
caseSensitivity = (
self.filterCaseSensitivityCheckBox.isChecked()
and Qt.CaseSensitive or Qt.CaseInsensitive)
regExp = QRegExp(self.filterPatternLineEdit.text(), caseSensitivity, syntax)
self.proxyModel.setFilterRegExp(regExp)
def dateFilterChanged(self):
self.proxyModel.setFilterMinimumDate(self.fromDateEdit.date())
self.proxyModel.setFilterMaximumDate(self.toDateEdit.date())
def addMail(model, subject, sender, date):
model.insertRow(0)
model.setData(model.index(0, 0), subject)
model.setData(model.index(0, 1), sender)
model.setData(model.index(0, 2), date)
def createMailModel(parent):
model = QStandardItemModel(0, 3, parent)
model.setHeaderData(0, Qt.Horizontal, "Subject")
model.setHeaderData(1, Qt.Horizontal, "Sender")
model.setHeaderData(2, Qt.Horizontal, "Date")
addMail(model, "Happy New Year!", "Grace K. <grace@software-inc.com>",
QDateTime(QDate(2006, 12, 31), QTime(17, 3)))
addMail(model, "Radically new concept", "Grace K. <grace@software-inc.com>",
QDateTime(QDate(2006, 12, 22), QTime(9, 44)))
addMail(model, "Accounts", "pascale@nospam.com",
QDateTime(QDate(2006, 12, 31), QTime(12, 50)))
addMail(model, "Expenses", "Joe Bloggs <joe@bloggs.com>",
QDateTime(QDate(2006, 12, 25), QTime(11, 39)))
addMail(model, "Re: Expenses", "Andy <andy@nospam.com>",
QDateTime(QDate(2007, 1, 2), QTime(16, 5)))
addMail(model, "Re: Accounts", "Joe Bloggs <joe@bloggs.com>",
QDateTime(QDate(2007, 1, 3), QTime(14, 18)))
addMail(model, "Re: Accounts", "Andy <andy@nospam.com>",
QDateTime(QDate(2007, 1, 3), QTime(14, 26)))
addMail(model, "Sports", "Linda Smith <linda.smith@nospam.com>",
QDateTime(QDate(2007, 1, 5), QTime(11, 33)))
addMail(model, "AW: Sports", "Rolf Newschweinstein <rolfn@nospam.com>",
QDateTime(QDate(2007, 1, 5), QTime(12, 0)))
addMail(model, "RE: Sports", "Petra Schmidt <petras@nospam.com>",
QDateTime(QDate(2007, 1, 5), QTime(12, 1)))
return model
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
window = Window()
window.setSourceModel(createMailModel(window))
window.show()
sys.exit(app.exec_())
我试图将这一行:self.dateInRange(self.sourceModel().data(index2)))
更改为:self.dateInRange(datetime.strptime(self.sourceModel().data(index2),"%Y/%m/%d %H:%M")))
以便将数据 TXT 转换为日期格式,这对于过滤非常有效,但如果我添加数据库的新行使用:
@pyqtSlot()
def on_pushButton_clicked(self):
self.add_record()
def add_record(self):
row = self.db_model.rowCount()
self.db_model.insertRow(row)
我哪里错了?
相关数据库部分:
class essaiFindDb():
def __init__(self):
self.db = QSqlDatabase.addDatabase("QSQLITE")
self.db.setDatabaseName("essai_find_database.db")
self.db.open()
query = QSqlQuery()
query.exec_('''CREATE TABLE Pilots_exp(id INTEGER PRIMARY KEY UNIQUE , pilot_1 TEXT,aircraft TEXT, date_time1 TEXT, date_time2 TEXT, total TEXT)''')
由于您没有提供MCVE,无法分析问题。
所以我的解决方案将基于您指出的内容:
- 第 3 列(date_time1)和第 4 列(date_time2)具有日期格式。
- 日期格式为
%Y/%m/%d %H:%M
(python日期时间格式)-yyyy/MM/dd hh:mm
(Qt格式)
- 日期过滤器应用于第 3 列。
如您所见,最简单的方法是第 3 列和第 4 列是 QDateTime,因此我将创建一个代理来进行转换。
另一方面,我将过滤器分为时间过滤器和文本过滤器,以便代码更清晰。
在这种情况下,我将应用级联代理:
┌-----------------┐ ┌---------------------------┐ ┌------------------------┐ ┌------------------------┐
| db_model | -> | text_to_QDateTime | -> | filter_date | -> | filter_text |
|(QSqlTableModel) | | (ConvertToDateProxyModel) | | (FilterDateProxyModel) | | (FilterTextProxyModel) |
└-----------------┘ └---------------------------┘ └------------------------┘ └------------------------┘
综合以上,解决方案是:
from PyQt5 import QtCore, QtGui, QtWidgets, QtSql
def createConnection():
db = QtSql.QSqlDatabase.addDatabase('QSQLITE')
db.setDatabaseName('essai_find_database.db')
if not db.open():
QtWidgets.QMessageBox.critical(None, QtWidgets.qApp.tr("Cannot open database"),
QtWidgets.qApp.tr("Unable to establish a database connection.\n"
"This example needs SQLite support. Please read "
"the Qt SQL driver documentation for information "
"how to build it.\n\n"
"Click Cancel to exit."),
QtWidgets.QMessageBox.Cancel)
return False
query = QtSql.QSqlQuery()
return query.exec_('''
CREATE TABLE IF NOT EXISTS Pilots_exp (
id INTEGER PRIMARY KEY UNIQUE ,
pilot_1 TEXT,aircraft TEXT,
date_time1 TEXT, date_time2 TEXT,
total TEXT)
''')
class ConvertToDateProxyModel(QtCore.QIdentityProxyModel):
def __init__(self, parent=None):
super(ConvertToDateProxyModel, self).__init__(parent)
self._columns = []
self._fmt = ""
def set_format(self, fmt):
self._fmt = fmt
def set_columns(self, columns):
self._columns = columns
def data(self, index, role=QtCore.Qt.DisplayRole):
v = super(ConvertToDateProxyModel, self).data(index, role)
if not index.isValid():
return
if index.column() in self._columns and self._fmt:
return QtCore.QDateTime.fromString(v, self._fmt)
return v
def setData(self, index, value, role=QtCore.Qt.EditRole):
if index.column() in self._columns and self._fmt:
sm = self.sourceModel()
ix = self.mapToSource(index)
return sm.setData(ix, value.toString(self._fmt), role)
return super(ConvertToDateProxyModel, self).setData(index, value, role)
class FilterDateProxyModel(QtCore.QSortFilterProxyModel):
def __init__(self, parent=None):
super(FilterDateProxyModel, self).__init__(parent)
self._from_date, self._to_date = QtCore.QDate(), QtCore.QDate()
def setRange(self, from_date, to_date):
self._from_date = from_date
self._to_date = to_date
self.invalidateFilter()
def filterAcceptsRow(self, sourceRow, sourceParent):
if any([not date.isValid() for date in (self._from_date, self._to_date,)]):
return True
ix = self.sourceModel().index(sourceRow, self.filterKeyColumn(), sourceParent)
dt = ix.data().date()
return self._from_date <= dt <= self._to_date
class FilterTextProxyModel(QtCore.QSortFilterProxyModel):
def __init__(self, parent=None):
super(FilterTextProxyModel, self).__init__(parent)
self._columns = []
def set_columns(self, columns):
self._columns = columns
self.invalidateFilter()
def filterAcceptsRow(self, sourceRow, sourceParent):
if not self._columns:
return True
values = []
for c in range(self.sourceModel().columnCount()):
if c in self._columns:
ix = self.sourceModel().index(sourceRow, c, sourceParent)
values.append(self.filterRegExp().indexIn(ix.data()) >= 0)
return any(values)
class AddDialog(QtWidgets.QDialog):
def __init__(self, formats, parent=None):
super(AddDialog, self).__init__(parent)
self._editors = dict()
flay = QtWidgets.QFormLayout(self)
for key, value in formats.items():
editor = self.create_editor_by_type(value)
flay.addRow(key, editor)
self._editors[key] = editor
buttonBox = QtWidgets.QDialogButtonBox()
buttonBox.setOrientation(QtCore.Qt.Horizontal)
buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
buttonBox.accepted.connect(self.accept)
buttonBox.rejected.connect(self.reject)
flay.addRow(buttonBox)
def create_editor_by_type(self, t):
editor = QtWidgets.QLineEdit()
if t == QtCore.QDateTime:
editor = QtWidgets.QDateTimeEdit(
dateTime= QtCore.QDateTime.currentDateTime(),
displayFormat="yyyy/MM/dd hh:mm",
calendarPopup=True
)
return editor
def get_value_from_editor(self, editor):
if isinstance(editor, QtWidgets.QLineEdit):
return editor.text()
if isinstance(editor, QtWidgets.QDateTimeEdit):
return editor.dateTime()
def get_values(self):
result = dict()
for key, editor in self._editors.items():
result[key] = self.get_value_from_editor(editor)
return result
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
self.db_model = QtSql.QSqlTableModel(self)
self.db_model.setTable("Pilots_exp")
self.db_model.select()
proxy_convert_to_date = ConvertToDateProxyModel(self)
proxy_convert_to_date.setSourceModel(self.db_model)
proxy_convert_to_date.set_columns([3, 4])
proxy_convert_to_date.set_format("yyyy/MM/dd hh:mm")
sourceGroupBox = QtWidgets.QGroupBox("Original Model")
sourceView = QtWidgets.QTableView(alternatingRowColors=True)
sourceView.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Stretch)
sourceView.verticalHeader().hide()
sourceView.setModel(proxy_convert_to_date)
sourceLayout = QtWidgets.QHBoxLayout(sourceGroupBox)
sourceLayout.addWidget(sourceView)
self._proxy_date = FilterDateProxyModel(self)
self._proxy_date.setFilterKeyColumn(3)
self._proxy_date.setSourceModel(proxy_convert_to_date)
self._proxy_filter = FilterTextProxyModel(self)
self._proxy_filter.setSourceModel(self._proxy_date)
self._proxy_filter.set_columns([1, 2])
proxyGroupBox = QtWidgets.QGroupBox("Sorted/Filtered Model")
proxyView = QtWidgets.QTableView()
proxyView.verticalHeader().hide()
proxyView.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Stretch)
proxyView.setSortingEnabled(True)
proxyView.setModel(self._proxy_filter)
proxyView.sortByColumn(1, QtCore.Qt.AscendingOrder)
proxyLayout = QtWidgets.QVBoxLayout(proxyGroupBox)
proxyLayout.addWidget(proxyView)
filterPatternLabel = QtWidgets.QLabel("&Filter pattern:")
self.filterPatternLineEdit = QtWidgets.QLineEdit(
"Grace|Sports",
textChanged=self.update_filter_text
)
filterPatternLabel.setBuddy(self.filterPatternLineEdit)
self.filterSyntaxComboBox = QtWidgets.QComboBox(
currentIndexChanged=self.update_filter_text
)
self.filterCaseSensitivityCheckBox = QtWidgets.QCheckBox(
"Case sensitive filter",
checked=True,
stateChanged=self.update_filter_text
)
self.fromDateEdit = QtWidgets.QDateEdit(
calendarPopup=True,
date=QtCore.QDate(2006, 12, 22),
dateChanged=self.update_filter_date
)
self.toDateEdit = QtWidgets.QDateEdit(
calendarPopup=True,
date=QtCore.QDate(2007, 1, 5),
dateChanged=self.update_filter_date
)
self.filterSyntaxComboBox.addItem("Regular expression", QtCore.QRegExp.RegExp)
self.filterSyntaxComboBox.addItem("Wildcard", QtCore.QRegExp.Wildcard)
self.filterSyntaxComboBox.addItem("Fixed string", QtCore.QRegExp.FixedString)
self.update_filter_text()
self.update_filter_date()
flay = QtWidgets.QFormLayout()
flay.addRow("F&rom:", self.fromDateEdit)
flay.addRow("&To:", self.toDateEdit)
hlay = QtWidgets.QHBoxLayout()
hlay.addWidget(filterPatternLabel)
hlay.addWidget(self.filterPatternLineEdit)
hlay.addWidget(self.filterSyntaxComboBox)
proxyLayout.addWidget(self.filterCaseSensitivityCheckBox)
proxyLayout.addLayout(hlay)
proxyLayout.addLayout(flay)
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(sourceGroupBox)
lay.addWidget(proxyGroupBox)
proxyView.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
proxyView.customContextMenuRequested.connect(self.on_customContextMenuRequested)
@QtCore.pyqtSlot(QtCore.QPoint)
def on_customContextMenuRequested(self, p):
view = self.sender()
menu = QtWidgets.QMenu()
add_row_action = menu.addAction("Add Row")
action = menu.exec_(view.viewport().mapToGlobal(p))
if action == add_row_action:
self.add_record()
@QtCore.pyqtSlot()
def update_filter_text(self):
syntax = self.filterSyntaxComboBox.currentData()
caseSensitivity = QtCore.Qt.CaseSensitive \
if self.filterCaseSensitivityCheckBox.isChecked() \
else QtCore.Qt.CaseInsensitive
regExp = QtCore.QRegExp(self.filterPatternLineEdit.text(), caseSensitivity, syntax)
self._proxy_filter.setFilterRegExp(regExp)
@QtCore.pyqtSlot()
def update_filter_date(self):
self._proxy_date.setRange(self.fromDateEdit.date(), self.toDateEdit.date())
def add_record(self):
d = {}
rec = self.db_model.record()
for i in range(rec.count()):
d[rec.fieldName(i)] = type(rec.value(i))
for i in (3, 4): d[rec.fieldName(i)] = QtCore.QDateTime
del d[rec.fieldName(0)]
dialog = AddDialog(d, self)
if dialog.exec_() == QtWidgets.QDialog.Accepted:
results = dialog.get_values()
for i in (3, 4):
k = rec.fieldName(i)
results[k] = results[k].toString("yyyy/MM/dd hh:mm")
for k, value in results.items():
rec.setValue(k, value)
self.db_model.insertRecord(-1, rec)
self.db_model.select()
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
if not createConnection():
sys.exit(-1)
w = Widget()
w.resize(960, 480)
w.show()
sys.exit(app.exec_())
我几乎一字不差地在 PyQt5 上重新实现了这个例子。这个例子中的问题是:它假设你在模型中输入一个 QDate,但在我的例子中,模型是从一个 sqlite 数据库中获取的,因此日期只是文本。
from PyQt5.QtCore import (QDate, QDateTime, QRegExp, QSortFilterProxyModel, Qt,
QTime)
from PyQt5.QtGui import QStandardItemModel
from PyQt5.QtWidgets import (QApplication, QCheckBox, QComboBox, QDateEdit,
QGridLayout, QGroupBox, QHBoxLayout, QLabel, QLineEdit, QTreeView,
QVBoxLayout, QWidget,QTableView)
class MySortFilterProxyModel(QSortFilterProxyModel):
def __init__(self, parent=None):
super(MySortFilterProxyModel, self).__init__(parent)
self.minDate = QDate()
self.maxDate = QDate()
def setFilterMinimumDate(self, date):
self.minDate = date
self.invalidateFilter()
def filterMinimumDate(self):
return self.minDate
def setFilterMaximumDate(self, date):
self.maxDate = date
self.invalidateFilter()
def filterMaximumDate(self):
return self.maxDate
def filterAcceptsRow(self, sourceRow, sourceParent):
index0 = self.sourceModel().index(sourceRow, 0, sourceParent)
index1 = self.sourceModel().index(sourceRow, 1, sourceParent)
index2 = self.sourceModel().index(sourceRow, 2, sourceParent)
return ((self.filterRegExp().indexIn(self.sourceModel().data(index0)) >= 0
or self.filterRegExp().indexIn(self.sourceModel().data(index1)) >= 0)
and self.dateInRange(self.sourceModel().data(index2)))
def dateInRange(self, date):
if isinstance(date, QDateTime):
date = date.date()
return ((not self.minDate.isValid() or date >= self.minDate)
and (not self.maxDate.isValid() or date <= self.maxDate))
class Window(QWidget):
def __init__(self):
super(Window, self).__init__()
self.proxyModel = MySortFilterProxyModel(self)
self.proxyModel.setDynamicSortFilter(True)
self.sourceView = QTreeView()
self.sourceView.setRootIsDecorated(False)
self.sourceView.setAlternatingRowColors(True)
sourceLayout = QHBoxLayout()
sourceLayout.addWidget(self.sourceView)
sourceGroupBox = QGroupBox("Original Model")
sourceGroupBox.setLayout(sourceLayout)
self.filterCaseSensitivityCheckBox = QCheckBox("Case sensitive filter")
self.filterCaseSensitivityCheckBox.setChecked(True)
self.filterPatternLineEdit = QLineEdit()
self.filterPatternLineEdit.setText("Grace|Sports")
filterPatternLabel = QLabel("&Filter pattern:")
filterPatternLabel.setBuddy(self.filterPatternLineEdit)
self.filterSyntaxComboBox = QComboBox()
self.filterSyntaxComboBox.addItem("Regular expression", QRegExp.RegExp)
self.filterSyntaxComboBox.addItem("Wildcard", QRegExp.Wildcard)
self.filterSyntaxComboBox.addItem("Fixed string", QRegExp.FixedString)
self.fromDateEdit = QDateEdit()
self.fromDateEdit.setDate(QDate(2006, 12, 22))
self.fromDateEdit.setCalendarPopup(True)
fromLabel = QLabel("F&rom:")
fromLabel.setBuddy(self.fromDateEdit)
self.toDateEdit = QDateEdit()
self.toDateEdit.setDate(QDate(2007, 1, 5))
self.toDateEdit.setCalendarPopup(True)
toLabel = QLabel("&To:")
toLabel.setBuddy(self.toDateEdit)
self.filterPatternLineEdit.textChanged.connect(self.textFilterChanged)
self.filterSyntaxComboBox.currentIndexChanged.connect(self.textFilterChanged)
self.filterCaseSensitivityCheckBox.toggled.connect(self.textFilterChanged)
self.fromDateEdit.dateChanged.connect(self.dateFilterChanged)
self.toDateEdit.dateChanged.connect(self.dateFilterChanged)
self.proxyView = QTableView()
# self.proxyView.setRootIsDecorated(False)
self.proxyView.setAlternatingRowColors(True)
self.proxyView.setModel(self.proxyModel)
self.proxyView.setSortingEnabled(True)
self.proxyView.sortByColumn(1, Qt.AscendingOrder)
self.textFilterChanged()
self.dateFilterChanged()
proxyLayout = QGridLayout()
proxyLayout.addWidget(self.proxyView, 0, 0, 1, 3)
proxyLayout.addWidget(filterPatternLabel, 1, 0)
proxyLayout.addWidget(self.filterPatternLineEdit, 1, 1)
proxyLayout.addWidget(self.filterSyntaxComboBox, 1, 2)
proxyLayout.addWidget(self.filterCaseSensitivityCheckBox, 2, 0, 1, 3)
proxyLayout.addWidget(fromLabel, 3, 0)
proxyLayout.addWidget(self.fromDateEdit, 3, 1, 1, 2)
proxyLayout.addWidget(toLabel, 4, 0)
proxyLayout.addWidget(self.toDateEdit, 4, 1, 1, 2)
proxyGroupBox = QGroupBox("Sorted/Filtered Model")
proxyGroupBox.setLayout(proxyLayout)
mainLayout = QVBoxLayout()
mainLayout.addWidget(sourceGroupBox)
mainLayout.addWidget(proxyGroupBox)
self.setLayout(mainLayout)
self.setWindowTitle("Custom Sort/Filter Model")
self.resize(500, 450)
def setSourceModel(self, model):
self.proxyModel.setSourceModel(model)
self.sourceView.setModel(model)
def textFilterChanged(self):
syntax = QRegExp.PatternSyntax(
self.filterSyntaxComboBox.itemData(
self.filterSyntaxComboBox.currentIndex()))
caseSensitivity = (
self.filterCaseSensitivityCheckBox.isChecked()
and Qt.CaseSensitive or Qt.CaseInsensitive)
regExp = QRegExp(self.filterPatternLineEdit.text(), caseSensitivity, syntax)
self.proxyModel.setFilterRegExp(regExp)
def dateFilterChanged(self):
self.proxyModel.setFilterMinimumDate(self.fromDateEdit.date())
self.proxyModel.setFilterMaximumDate(self.toDateEdit.date())
def addMail(model, subject, sender, date):
model.insertRow(0)
model.setData(model.index(0, 0), subject)
model.setData(model.index(0, 1), sender)
model.setData(model.index(0, 2), date)
def createMailModel(parent):
model = QStandardItemModel(0, 3, parent)
model.setHeaderData(0, Qt.Horizontal, "Subject")
model.setHeaderData(1, Qt.Horizontal, "Sender")
model.setHeaderData(2, Qt.Horizontal, "Date")
addMail(model, "Happy New Year!", "Grace K. <grace@software-inc.com>",
QDateTime(QDate(2006, 12, 31), QTime(17, 3)))
addMail(model, "Radically new concept", "Grace K. <grace@software-inc.com>",
QDateTime(QDate(2006, 12, 22), QTime(9, 44)))
addMail(model, "Accounts", "pascale@nospam.com",
QDateTime(QDate(2006, 12, 31), QTime(12, 50)))
addMail(model, "Expenses", "Joe Bloggs <joe@bloggs.com>",
QDateTime(QDate(2006, 12, 25), QTime(11, 39)))
addMail(model, "Re: Expenses", "Andy <andy@nospam.com>",
QDateTime(QDate(2007, 1, 2), QTime(16, 5)))
addMail(model, "Re: Accounts", "Joe Bloggs <joe@bloggs.com>",
QDateTime(QDate(2007, 1, 3), QTime(14, 18)))
addMail(model, "Re: Accounts", "Andy <andy@nospam.com>",
QDateTime(QDate(2007, 1, 3), QTime(14, 26)))
addMail(model, "Sports", "Linda Smith <linda.smith@nospam.com>",
QDateTime(QDate(2007, 1, 5), QTime(11, 33)))
addMail(model, "AW: Sports", "Rolf Newschweinstein <rolfn@nospam.com>",
QDateTime(QDate(2007, 1, 5), QTime(12, 0)))
addMail(model, "RE: Sports", "Petra Schmidt <petras@nospam.com>",
QDateTime(QDate(2007, 1, 5), QTime(12, 1)))
return model
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
window = Window()
window.setSourceModel(createMailModel(window))
window.show()
sys.exit(app.exec_())
我试图将这一行:self.dateInRange(self.sourceModel().data(index2)))
更改为:self.dateInRange(datetime.strptime(self.sourceModel().data(index2),"%Y/%m/%d %H:%M")))
以便将数据 TXT 转换为日期格式,这对于过滤非常有效,但如果我添加数据库的新行使用:
@pyqtSlot()
def on_pushButton_clicked(self):
self.add_record()
def add_record(self):
row = self.db_model.rowCount()
self.db_model.insertRow(row)
我哪里错了?
相关数据库部分:
class essaiFindDb():
def __init__(self):
self.db = QSqlDatabase.addDatabase("QSQLITE")
self.db.setDatabaseName("essai_find_database.db")
self.db.open()
query = QSqlQuery()
query.exec_('''CREATE TABLE Pilots_exp(id INTEGER PRIMARY KEY UNIQUE , pilot_1 TEXT,aircraft TEXT, date_time1 TEXT, date_time2 TEXT, total TEXT)''')
由于您没有提供MCVE,无法分析问题。
所以我的解决方案将基于您指出的内容:
- 第 3 列(date_time1)和第 4 列(date_time2)具有日期格式。
- 日期格式为
%Y/%m/%d %H:%M
(python日期时间格式)-yyyy/MM/dd hh:mm
(Qt格式) - 日期过滤器应用于第 3 列。
如您所见,最简单的方法是第 3 列和第 4 列是 QDateTime,因此我将创建一个代理来进行转换。
另一方面,我将过滤器分为时间过滤器和文本过滤器,以便代码更清晰。
在这种情况下,我将应用级联代理:
┌-----------------┐ ┌---------------------------┐ ┌------------------------┐ ┌------------------------┐
| db_model | -> | text_to_QDateTime | -> | filter_date | -> | filter_text |
|(QSqlTableModel) | | (ConvertToDateProxyModel) | | (FilterDateProxyModel) | | (FilterTextProxyModel) |
└-----------------┘ └---------------------------┘ └------------------------┘ └------------------------┘
综合以上,解决方案是:
from PyQt5 import QtCore, QtGui, QtWidgets, QtSql
def createConnection():
db = QtSql.QSqlDatabase.addDatabase('QSQLITE')
db.setDatabaseName('essai_find_database.db')
if not db.open():
QtWidgets.QMessageBox.critical(None, QtWidgets.qApp.tr("Cannot open database"),
QtWidgets.qApp.tr("Unable to establish a database connection.\n"
"This example needs SQLite support. Please read "
"the Qt SQL driver documentation for information "
"how to build it.\n\n"
"Click Cancel to exit."),
QtWidgets.QMessageBox.Cancel)
return False
query = QtSql.QSqlQuery()
return query.exec_('''
CREATE TABLE IF NOT EXISTS Pilots_exp (
id INTEGER PRIMARY KEY UNIQUE ,
pilot_1 TEXT,aircraft TEXT,
date_time1 TEXT, date_time2 TEXT,
total TEXT)
''')
class ConvertToDateProxyModel(QtCore.QIdentityProxyModel):
def __init__(self, parent=None):
super(ConvertToDateProxyModel, self).__init__(parent)
self._columns = []
self._fmt = ""
def set_format(self, fmt):
self._fmt = fmt
def set_columns(self, columns):
self._columns = columns
def data(self, index, role=QtCore.Qt.DisplayRole):
v = super(ConvertToDateProxyModel, self).data(index, role)
if not index.isValid():
return
if index.column() in self._columns and self._fmt:
return QtCore.QDateTime.fromString(v, self._fmt)
return v
def setData(self, index, value, role=QtCore.Qt.EditRole):
if index.column() in self._columns and self._fmt:
sm = self.sourceModel()
ix = self.mapToSource(index)
return sm.setData(ix, value.toString(self._fmt), role)
return super(ConvertToDateProxyModel, self).setData(index, value, role)
class FilterDateProxyModel(QtCore.QSortFilterProxyModel):
def __init__(self, parent=None):
super(FilterDateProxyModel, self).__init__(parent)
self._from_date, self._to_date = QtCore.QDate(), QtCore.QDate()
def setRange(self, from_date, to_date):
self._from_date = from_date
self._to_date = to_date
self.invalidateFilter()
def filterAcceptsRow(self, sourceRow, sourceParent):
if any([not date.isValid() for date in (self._from_date, self._to_date,)]):
return True
ix = self.sourceModel().index(sourceRow, self.filterKeyColumn(), sourceParent)
dt = ix.data().date()
return self._from_date <= dt <= self._to_date
class FilterTextProxyModel(QtCore.QSortFilterProxyModel):
def __init__(self, parent=None):
super(FilterTextProxyModel, self).__init__(parent)
self._columns = []
def set_columns(self, columns):
self._columns = columns
self.invalidateFilter()
def filterAcceptsRow(self, sourceRow, sourceParent):
if not self._columns:
return True
values = []
for c in range(self.sourceModel().columnCount()):
if c in self._columns:
ix = self.sourceModel().index(sourceRow, c, sourceParent)
values.append(self.filterRegExp().indexIn(ix.data()) >= 0)
return any(values)
class AddDialog(QtWidgets.QDialog):
def __init__(self, formats, parent=None):
super(AddDialog, self).__init__(parent)
self._editors = dict()
flay = QtWidgets.QFormLayout(self)
for key, value in formats.items():
editor = self.create_editor_by_type(value)
flay.addRow(key, editor)
self._editors[key] = editor
buttonBox = QtWidgets.QDialogButtonBox()
buttonBox.setOrientation(QtCore.Qt.Horizontal)
buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
buttonBox.accepted.connect(self.accept)
buttonBox.rejected.connect(self.reject)
flay.addRow(buttonBox)
def create_editor_by_type(self, t):
editor = QtWidgets.QLineEdit()
if t == QtCore.QDateTime:
editor = QtWidgets.QDateTimeEdit(
dateTime= QtCore.QDateTime.currentDateTime(),
displayFormat="yyyy/MM/dd hh:mm",
calendarPopup=True
)
return editor
def get_value_from_editor(self, editor):
if isinstance(editor, QtWidgets.QLineEdit):
return editor.text()
if isinstance(editor, QtWidgets.QDateTimeEdit):
return editor.dateTime()
def get_values(self):
result = dict()
for key, editor in self._editors.items():
result[key] = self.get_value_from_editor(editor)
return result
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
self.db_model = QtSql.QSqlTableModel(self)
self.db_model.setTable("Pilots_exp")
self.db_model.select()
proxy_convert_to_date = ConvertToDateProxyModel(self)
proxy_convert_to_date.setSourceModel(self.db_model)
proxy_convert_to_date.set_columns([3, 4])
proxy_convert_to_date.set_format("yyyy/MM/dd hh:mm")
sourceGroupBox = QtWidgets.QGroupBox("Original Model")
sourceView = QtWidgets.QTableView(alternatingRowColors=True)
sourceView.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Stretch)
sourceView.verticalHeader().hide()
sourceView.setModel(proxy_convert_to_date)
sourceLayout = QtWidgets.QHBoxLayout(sourceGroupBox)
sourceLayout.addWidget(sourceView)
self._proxy_date = FilterDateProxyModel(self)
self._proxy_date.setFilterKeyColumn(3)
self._proxy_date.setSourceModel(proxy_convert_to_date)
self._proxy_filter = FilterTextProxyModel(self)
self._proxy_filter.setSourceModel(self._proxy_date)
self._proxy_filter.set_columns([1, 2])
proxyGroupBox = QtWidgets.QGroupBox("Sorted/Filtered Model")
proxyView = QtWidgets.QTableView()
proxyView.verticalHeader().hide()
proxyView.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Stretch)
proxyView.setSortingEnabled(True)
proxyView.setModel(self._proxy_filter)
proxyView.sortByColumn(1, QtCore.Qt.AscendingOrder)
proxyLayout = QtWidgets.QVBoxLayout(proxyGroupBox)
proxyLayout.addWidget(proxyView)
filterPatternLabel = QtWidgets.QLabel("&Filter pattern:")
self.filterPatternLineEdit = QtWidgets.QLineEdit(
"Grace|Sports",
textChanged=self.update_filter_text
)
filterPatternLabel.setBuddy(self.filterPatternLineEdit)
self.filterSyntaxComboBox = QtWidgets.QComboBox(
currentIndexChanged=self.update_filter_text
)
self.filterCaseSensitivityCheckBox = QtWidgets.QCheckBox(
"Case sensitive filter",
checked=True,
stateChanged=self.update_filter_text
)
self.fromDateEdit = QtWidgets.QDateEdit(
calendarPopup=True,
date=QtCore.QDate(2006, 12, 22),
dateChanged=self.update_filter_date
)
self.toDateEdit = QtWidgets.QDateEdit(
calendarPopup=True,
date=QtCore.QDate(2007, 1, 5),
dateChanged=self.update_filter_date
)
self.filterSyntaxComboBox.addItem("Regular expression", QtCore.QRegExp.RegExp)
self.filterSyntaxComboBox.addItem("Wildcard", QtCore.QRegExp.Wildcard)
self.filterSyntaxComboBox.addItem("Fixed string", QtCore.QRegExp.FixedString)
self.update_filter_text()
self.update_filter_date()
flay = QtWidgets.QFormLayout()
flay.addRow("F&rom:", self.fromDateEdit)
flay.addRow("&To:", self.toDateEdit)
hlay = QtWidgets.QHBoxLayout()
hlay.addWidget(filterPatternLabel)
hlay.addWidget(self.filterPatternLineEdit)
hlay.addWidget(self.filterSyntaxComboBox)
proxyLayout.addWidget(self.filterCaseSensitivityCheckBox)
proxyLayout.addLayout(hlay)
proxyLayout.addLayout(flay)
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(sourceGroupBox)
lay.addWidget(proxyGroupBox)
proxyView.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
proxyView.customContextMenuRequested.connect(self.on_customContextMenuRequested)
@QtCore.pyqtSlot(QtCore.QPoint)
def on_customContextMenuRequested(self, p):
view = self.sender()
menu = QtWidgets.QMenu()
add_row_action = menu.addAction("Add Row")
action = menu.exec_(view.viewport().mapToGlobal(p))
if action == add_row_action:
self.add_record()
@QtCore.pyqtSlot()
def update_filter_text(self):
syntax = self.filterSyntaxComboBox.currentData()
caseSensitivity = QtCore.Qt.CaseSensitive \
if self.filterCaseSensitivityCheckBox.isChecked() \
else QtCore.Qt.CaseInsensitive
regExp = QtCore.QRegExp(self.filterPatternLineEdit.text(), caseSensitivity, syntax)
self._proxy_filter.setFilterRegExp(regExp)
@QtCore.pyqtSlot()
def update_filter_date(self):
self._proxy_date.setRange(self.fromDateEdit.date(), self.toDateEdit.date())
def add_record(self):
d = {}
rec = self.db_model.record()
for i in range(rec.count()):
d[rec.fieldName(i)] = type(rec.value(i))
for i in (3, 4): d[rec.fieldName(i)] = QtCore.QDateTime
del d[rec.fieldName(0)]
dialog = AddDialog(d, self)
if dialog.exec_() == QtWidgets.QDialog.Accepted:
results = dialog.get_values()
for i in (3, 4):
k = rec.fieldName(i)
results[k] = results[k].toString("yyyy/MM/dd hh:mm")
for k, value in results.items():
rec.setValue(k, value)
self.db_model.insertRecord(-1, rec)
self.db_model.select()
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
if not createConnection():
sys.exit(-1)
w = Widget()
w.resize(960, 480)
w.show()
sys.exit(app.exec_())