问题描述
我有一个带有列标题过滤器的 QTreeView,但想使用 QTableView。
问题:我不知道如何重新设计 QTableView 的标头功能。
如果我只是将使用的类从 QTreeView() 切换到 QTableView() 我会得到很多错误
喜欢AttributeError: 'QTableView' object has no attribute 'setHeader'
我想构建一个带有列标题过滤器的 TableView,如下所示: (由“DB Browser for SQLite”提供)
在第一个回复的返工后,当我删除时,我有以下内容
self.treeView.verticalHeader().hide()
:
MRE:
import sys
import re
from PyQt5 import QtWidgets,QtGui,QtCore,Qtsql
COUNT_PERS_COLS = 3
col_persID,col_persLAST_NAME,col_persFirsT_NAME = range(COUNT_PERS_COLS)
db = Qtsql.QsqlDatabase.addDatabase("QsqlITE")
db.setDatabaseName(':memory:')
modelQuery = Qtsql.QsqlQueryModel()
modelTable = Qtsql.QsqlRelationalTableModel()
def _human_key(key):
parts = re.split(r'(\d*\.\d+|\d+)',key)
return tuple((e.swapcase() if i % 2 == 0 else float(e))
for i,e in enumerate(parts))
class FilterHeader(QtWidgets.QHeaderView):
filteractivated = QtCore.pyqtSignal()
def __init__(self,parent):
super().__init__(QtCore.Qt.Horizontal,parent)
self._editors = []
self._padding = 4
self.setStretchLastSection(True)
self.setSectionResizeMode(QtWidgets.QHeaderView.Stretch)
self.setDefaultAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
self.setSortIndicatorShown(False)
self.setSectionsMovable(True)
self.sectionResized.connect(self.adjustPositions)
parent.horizontalScrollBar().valueChanged.connect(self.adjustPositions)
def setFilterBoxes(self,count):
while self._editors:
editor = self._editors.pop()
editor.deleteLater()
for index in range(count):
editor = QtWidgets.QLineEdit(self.parent())
editor.setPlaceholderText('Filter')
editor.setClearButtonEnabled(True)
editor.returnpressed.connect(self.filteractivated.emit)
self._editors.append(editor)
self.adjustPositions()
def sizeHint(self):
size = super().sizeHint()
if self._editors:
height = self._editors[0].sizeHint().height()
size.setHeight(size.height() + height + self._padding)
return size
def updateGeometries(self):
if self._editors:
height = self._editors[0].sizeHint().height()
self.setViewportMargins(0,height + self._padding)
else:
self.setViewportMargins(0,0)
super().updateGeometries()
self.adjustPositions()
def adjustPositions(self):
for index,editor in enumerate(self._editors):
height = editor.sizeHint().height()
editor.move(
self.sectionPosition(index) - self.offset() + 2,height + (self._padding // 2))
editor.resize(self.sectionSize(index),height)
def filterText(self,index):
if 0 <= index < len(self._editors):
return self._editors[index].text()
return ''
def setFilterText(self,index,text):
if 0 <= index < len(self._editors):
self._editors[index].setText(text)
def clearFilters(self):
for editor in self._editors:
editor.clear()
class HumanProxyModel(QtCore.QSortFilterProxyModel):
def lessthan(self,source_left,source_right):
data_left = source_left.data()
data_right = source_right.data()
if type(data_left) == type(data_right) == str:
return _human_key(data_left) < _human_key(data_right)
return super(HumanProxyModel,self).lessthan(source_left,source_right)
@property
def filters(self):
if not hasattr(self,"_filters"):
self._filters = []
return self._filters
@filters.setter
def filters(self,filters):
print(f"filters() called.")
self._filters = filters
self.invalidatefilter()
def filteracceptsRow(self,sourceRow,sourceParent):
for i,text in self.filters:
if 0 <= i < self.columnCount():
ix = self.sourceModel().index(sourceRow,i,sourceParent)
data = ix.data()
if text not in data:
return False
return True
class winMain(QtWidgets.QMainWindow):
def __init__(self,parent=None):
super().__init__(parent)
self.setupUi()
self.setGeometry(300,200,700,500)
self.show()
def createPersonModel(self,parent):
model = QtGui.QStandardItemmodel(0,COUNT_PERS_COLS,parent)
model.setHorizontalHeaderLabels(['ID','Last Name','First Name'])
return model
def addPerson(self,model,id,last_name,first_name):
model.insertRow(0)
model.setData(model.index(0,col_persID),id)
model.setData(model.index(0,col_persLAST_NAME),last_name)
model.setData(model.index(0,col_persFirsT_NAME),first_name)
def handleFilteractivated(self):
header = self.treeView.header()
filters = []
for i in range(header.count()):
text = header.filterText(i)
if text:
filters.append((i,text))
proxy = self.treeView.model()
proxy.filters = filters
def setupUi(self):
self.centralwidget = QtWidgets.QWidget(self)
self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget)
self.treeView = QtWidgets.QTreeView(self.centralwidget)
self.treeView.setSortingEnabled(True)
self.treeView.setAlternatingRowColors(True)
self.treeView.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
self.treeView.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
self.treeView.setAnimated(True)
self.treeView.setItemsExpandable(True)
self.horizontalLayout.addWidget(self.treeView)
self.setCentralWidget(self.centralwidget)
header = FilterHeader(self.treeView)
self.treeView.setHeader(header)
self.statusBar = QtWidgets.QStatusBar()
self.setStatusBar(self.statusBar)
modelTable.setTable("person")
self.treeView.setModel(modelTable)
proxy = HumanProxyModel(self)
proxy.setSourceModel(modelTable)
self.treeView.setModel(proxy)
header.setFilterBoxes(modelTable.columnCount())
header.filteractivated.connect(self.handleFilteractivated)
def create_sample_data():
modelQuery.setQuery("""CREATE TABLE IF NOT EXISTS country (
id INTEGER PRIMARY KEY UNIQUE NOT NULL,name TEXT
)""")
modelQuery.setQuery("""CREATE TABLE IF NOT EXISTS person (
id INTEGER PRIMARY KEY UNIQUE NOT NULL,persId TEXT,lastName TEXT,firstName TEXT,country_id INTEGER NOT NULL DEFAULT 3,FOREIGN KEY (country_id) REFERENCES country(id)
)""")
modelQuery.setQuery("INSERT INTO country (id,name) VALUES (0,'None')")
modelQuery.setQuery("INSERT INTO country (id,name) VALUES (1,'Angola')")
modelQuery.setQuery("INSERT INTO country (id,name) VALUES (2,'Serbia')")
modelQuery.setQuery("INSERT INTO country (id,name) VALUES (3,'Georgia')")
modelQuery.setQuery("INSERT INTO person (id,persId,lastName,firstName,country_id) VALUES (1,'1001','Martin','Robert',1)")
modelQuery.setQuery("INSERT INTO person (id,country_id) VALUES (2,'1002','Smith','Brad',2)")
modelQuery.setQuery("INSERT INTO person (id,country_id) VALUES (3,'1003','Angelina',3)")
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
create_sample_data()
window = winMain()
sys.exit(app.exec_())
解决方法
QTreeView 只有一个标题(水平标题),而 QTableView 有 2 个。此外,QTableView 没有分支,因此它也没有 setAnimated()
和 setItemsExpandable()
方法。
def handleFilterActivated(self):
# header = self.treeView.header()
header = self.treeView.horizontalHeader()
filters = []
for i in range(header.count()):
text = header.filterText(i)
if text:
filters.append((i,text))
proxy = self.treeView.model()
proxy.filters = filters
def setupUi(self):
self.centralwidget = QtWidgets.QWidget(self)
self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget)
self.treeView = QtWidgets.QTableView(self.centralwidget)
self.treeView.setSortingEnabled(True)
self.treeView.setAlternatingRowColors(True)
self.treeView.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
self.treeView.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
# self.treeView.setAnimated(True)
# self.treeView.setItemsExpandable(True)
self.horizontalLayout.addWidget(self.treeView)
self.setCentralWidget(self.centralwidget)
header = FilterHeader(self.treeView)
# self.treeView.setHeader(header)
self.treeView.setHorizontalHeader(header)
self.treeView.verticalHeader().hide()
self.statusBar = QtWidgets.QStatusBar()
self.setStatusBar(self.statusBar)
modelTable.setTable("person")
modelTable.select()
self.treeView.setModel(modelTable)
proxy = HumanProxyModel(self)
proxy.setSourceModel(modelTable)
self.treeView.setModel(proxy)
header.setFilterBoxes(modelTable.columnCount())
header.filterActivated.connect(self.handleFilterActivated)