问题描述
下面是示例代码:
class FilterProxyModel(QtCore.QSortFilterProxyModel):
def __init__(self,parent=None):
super().__init__(parent)
self._filter_value = None
@property
def filter_value(self):
return self._filter_value
@filter_value.setter
def filter_value(self,value):
self._filter_value = value
self.invalidatefilter()
def filteracceptsRow(self,sourceRow,sourceParent):
if self.filter_value is None:
return super().filteracceptsRow(sourceRow,sourceParent)
if self.filterKeyColumn() >= 0:
value = (
self.sourceModel()
.index(sourceRow,self.filterKeyColumn(),sourceParent)
.data(self.filterRole())
)
return value == self.filter_value
for column in range(self.columnCount()):
value = (
self.sourceModel()
.index(sourceRow,column,sourceParent)
.data(self.filterRole())
)
if value == self.filter_value:
return True
return False
def setFilterRegExp(self,filter):
self.filter_value = None
super().setFilterRegExp(filter)
class UI(QMainWindow):
def __init__(self):
super(UI,self).__init__()
uic.loadUi("tableview.ui",self)
self.show()
db = QsqlDatabase.addDatabase('QsqlITE')
db.setDatabaseName('book.db')
db.open()
self.model = Qtsql.QsqlTableModel(self)
self.model.setTable("card")
self.model.select()
self.tableView.setModel(self.model)
self.proxy = FilterProxyModel(self)
self.proxy.setSourceModel(self.model)
self.tableView.setModel(self.proxy)
self.model.select()
self.edit.clicked.connect(self.edit_items)
self.refresh.clicked.connect(self.refresh_table)
column_names = ["Name","Age","Gender"]
self.comboBox.addItems([x for x in column_names])
self.horizontalHeader = self.tableView.horizontalHeader()
self.horizontalHeader.sectionClicked.connect(
self.tableView_horizontalHeader_sectionClicked
)
self.lineEdit.textChanged.connect(self.lineEdit_textChanged)
def tableView_horizontalHeader_sectionClicked(self,logicalIndex):
menu = QtWidgets.QMenu(self)
values = []
for row in range(self.model.rowCount()):
value = self.model.index(row,logicalIndex).data(self.proxy.filterRole())
values.append(value)
action_all = QtWidgets.QAction("All",self)
action_all.setData(None)
menu.addAction(action_all)
menu.addSeparator()
for value in sorted(list(set(values))):
action = QtWidgets.QAction(str(value),self)
action.setData(value)
menu.addAction(action)
headerPos = self.tableView.mapToGlobal(self.horizontalHeader.pos())
posY = headerPos.y() + self.horizontalHeader.height()
posX = headerPos.x() + self.horizontalHeader.sectionPosition(logicalIndex)
action = menu.exec_(QtCore.QPoint(posX,posY))
if action is not None:
self.proxy.setFilterKeyColumn(logicalIndex)
self.proxy.filter_value = action.data()
def lineEdit_textChanged(self):
search = QtCore.QRegExp( self.lineEdit.text(),QtCore.Qt.CaseInsensitive,QtCore.QRegExp.RegExp
)
self.proxy.setFilterKeyColumn(self.comboBox.currentIndex() - 1)
self.proxy.setFilterRegExp(search)
def edit_items(self):
if not self.model.rowCount():
return
index = self.tableView.currentIndex()
if index.isValid():
row = index.row()
else:
row = 0
dialog = QtWidgets.QDialog()
dialog.setwindowTitle("Edit Window")
layout = QtWidgets.QVBoxLayout(dialog)
formlayout = QtWidgets.qformlayout()
layout.addLayout(formlayout)
name_line = QtWidgets.QLineEdit(self.model.index(row,0).data())
formlayout.addRow('Name',name_line)
name_line.setReadOnly(True)
age_edit = QtWidgets.QSpinBox()
formlayout.addRow('Age',age_edit)
age_edit.setFocus()
age_edit.setValue(self.model.index(row,1).data())
genders = 'M','F'
gender_combo = QtWidgets.QComboBox()
formlayout.addRow('Gender',gender_combo)
gender_combo.addItems(genders)
gender = self.model.index(row,2).data()
if gender and gender.upper() in genders:
gender_combo.setCurrentIndex(genders.index(gender.upper()))
else:
gender_combo.setCurrentIndex(-1)
updateButton = QtWidgets.QPushButton('Update')
layout.addWidget(updateButton)
updateButton.clicked.connect(dialog.accept)
if not dialog.exec_():
return
self.model.setData(self.model.index(row,1),age_edit.value(),QtCore.Qt.EditRole)
if gender_combo.currentIndex() >= 0:
self.model.setData(self.model.index(row,2),gender_combo.currentText(),QtCore.Qt.EditRole)
# submit all changes to the database
self.model.submitAll()
def refresh_table(self):
print("rsfersh")
这很好,在使用FilterProxyModel过滤列之前。 问题是:过滤列后,没有将确切的行放入“编辑窗口”。例如,它不是从tableview获取数据库索引行。
下面是参考图像。
如上图,在过滤器之后,我想编辑第4列(“ name4”),但它占用第1列(“ name1”)。过滤后如何编辑必填列。
解决方法
QTableView的currentIndex方法返回与在模型中建立的模型相关联的QModelIndex,在这种情况下,它是“ self.proxy”,而不是“ self.model”。另一方面,在这些情况下,使用QDataWidgetMapper简化了映射列的任务:
import sys
from PyQt5 import QtCore,QtWidgets,QtSql,uic
class FilterProxyModel(QtCore.QSortFilterProxyModel):
def __init__(self,parent=None):
super().__init__(parent)
self._filter_value = None
@property
def filter_value(self):
return self._filter_value
@filter_value.setter
def filter_value(self,value):
self._filter_value = value
self.invalidateFilter()
def filterAcceptsRow(self,sourceRow,sourceParent):
if self.filter_value is None:
return super().filterAcceptsRow(sourceRow,sourceParent)
if self.filterKeyColumn() >= 0:
value = (
self.sourceModel()
.index(sourceRow,self.filterKeyColumn(),sourceParent)
.data(self.filterRole())
)
return value == self.filter_value
for column in range(self.columnCount()):
value = (
self.sourceModel()
.index(sourceRow,column,sourceParent)
.data(self.filterRole())
)
if value == self.filter_value:
return True
return False
def setFilterRegExp(self,filter):
self.filter_value = None
super().setFilterRegExp(filter)
class UI(QtWidgets.QMainWindow):
def __init__(self):
super(UI,self).__init__()
uic.loadUi("tableview.ui",self)
self.tableView.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
db = QtSql.QSqlDatabase.addDatabase("QSQLITE")
db.setDatabaseName("book.db")
db.open()
self.model = QtSql.QSqlTableModel(self)
self.model.setTable("card")
self.model.select()
self.proxy = FilterProxyModel(self)
self.proxy.setSourceModel(self.model)
self.tableView.setModel(self.proxy)
self.model.select()
self.edit.clicked.connect(self.edit_items)
self.refresh.clicked.connect(self.refresh_table)
r = self.model.record()
column_names = [r.field(i).name().title() for i in range(r.count())]
self.comboBox.addItems([x for x in column_names])
self.horizontalHeader = self.tableView.horizontalHeader()
self.horizontalHeader.sectionClicked.connect(
self.tableView_horizontalHeader_sectionClicked
)
self.lineEdit.textChanged.connect(self.lineEdit_textChanged)
def tableView_horizontalHeader_sectionClicked(self,logicalIndex):
menu = QtWidgets.QMenu(self)
values = []
for row in range(self.model.rowCount()):
value = self.model.index(row,logicalIndex).data(self.proxy.filterRole())
values.append(value)
action_all = QtWidgets.QAction("All",self)
action_all.setData(None)
menu.addAction(action_all)
menu.addSeparator()
for value in sorted(list(set(values))):
action = QtWidgets.QAction(str(value),self)
action.setData(value)
menu.addAction(action)
headerPos = self.tableView.mapToGlobal(self.horizontalHeader.pos())
posY = headerPos.y() + self.horizontalHeader.height()
posX = headerPos.x() + self.horizontalHeader.sectionPosition(logicalIndex)
action = menu.exec_(QtCore.QPoint(posX,posY))
if action is not None:
self.proxy.setFilterKeyColumn(logicalIndex)
self.proxy.filter_value = action.data()
def lineEdit_textChanged(self):
search = QtCore.QRegExp(
self.lineEdit.text(),QtCore.Qt.CaseInsensitive,QtCore.QRegExp.RegExp
)
self.proxy.setFilterKeyColumn(self.comboBox.currentIndex())
self.proxy.setFilterRegExp(search)
def edit_items(self):
if not self.model.rowCount():
return
index = self.tableView.currentIndex()
if index.isValid():
row = index.row()
else:
row = 0
name_line = QtWidgets.QLineEdit(readOnly=True)
age_edit = QtWidgets.QSpinBox()
gender_combo = QtWidgets.QComboBox()
genders = "M","F"
gender_combo.addItems(genders)
updateButton = QtWidgets.QPushButton("Update")
mapper = QtWidgets.QDataWidgetMapper()
mapper.setSubmitPolicy(QtWidgets.QDataWidgetMapper.ManualSubmit)
mapper.setModel(self.tableView.model())
mapper.addMapping(name_line,0)
mapper.addMapping(age_edit,1)
mapper.addMapping(gender_combo,2)
mapper.setCurrentIndex(row)
dialog = QtWidgets.QDialog()
dialog.setWindowTitle("Edit Window")
layout = QtWidgets.QVBoxLayout(dialog)
formLayout = QtWidgets.QFormLayout()
layout.addLayout(formLayout)
formLayout.addRow("Name",name_line)
formLayout.addRow("Age",age_edit)
formLayout.addRow("Gender",gender_combo)
layout.addWidget(updateButton)
updateButton.clicked.connect(dialog.accept)
if dialog.exec_():
mapper.submit()
def refresh_table(self):
print("refresh")
def main():
app = QtWidgets.QApplication(sys.argv)
w = UI()
w.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()