我有两个问题:
我想知道这是否是在单列树视图上进行搜索/过滤的正确方法。我觉得我的很多复制/粘贴可能包含不必要的东西。是QSortFilterProxyModel
的子类中的所有代码和search_text_changed
方法中的代码都需要吗?我不觉得需要正则表达式,因为我设置filter-proxy来忽略区分大小写。
如何使用当双击树视图项时,信号会发出一个字符串列表,其中包含所点击的项目的字符串及其所有祖先的递归信息?例如,如果我双击" Birds",它将返回['Birds','Animals']
;如果我双击动物"它就会返回['Animals']
。
import os, sys
from PySide import QtCore, QtGui
tags = {
"Animals": [
"Birds",
"Various"
],
"Brick": [
"Blocks",
"Special"
],
"Manmade": [
"Air Conditioners",
"Audio Equipment"
],
"Food": [
"Fruit",
"Grains and Seeds"
]
}
class SearchProxyModel(QtGui.QSortFilterProxyModel):
def __init__(self, parent=None):
super(SearchProxyModel, self).__init__(parent)
self.text = ''
# Recursive search
def _accept_index(self, idx):
if idx.isValid():
text = idx.data(role=QtCore.Qt.DisplayRole).lower()
condition = text.find(self.text) >= 0
if condition:
return True
for childnum in range(idx.model().rowCount(parent=idx)):
if self._accept_index(idx.model().index(childnum, 0, parent=idx)):
return True
return False
def filterAcceptsRow(self, sourceRow, sourceParent):
# Only first column in model for search
idx = self.sourceModel().index(sourceRow, 0, sourceParent)
return self._accept_index(idx)
def lessThan(self, left, right):
leftData = self.sourceModel().data(left)
rightData = self.sourceModel().data(right)
return leftData < rightData
class TagsBrowserWidget(QtGui.QWidget):
clickedTag = QtCore.Signal(list)
def __init__(self, parent=None):
super(TagsBrowserWidget, self).__init__(parent)
self.resize(300,500)
# controls
self.ui_search = QtGui.QLineEdit()
self.ui_search.setPlaceholderText('Search...')
self.tags_model = SearchProxyModel()
self.tags_model.setSourceModel(QtGui.QStandardItemModel())
self.tags_model.setDynamicSortFilter(True)
self.tags_model.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive)
self.ui_tags = QtGui.QTreeView()
self.ui_tags.setSortingEnabled(True)
self.ui_tags.sortByColumn(0, QtCore.Qt.AscendingOrder)
self.ui_tags.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
self.ui_tags.setHeaderHidden(True)
self.ui_tags.setRootIsDecorated(True)
self.ui_tags.setUniformRowHeights(True)
self.ui_tags.setModel(self.tags_model)
# layout
main_layout = QtGui.QVBoxLayout()
main_layout.addWidget(self.ui_search)
main_layout.addWidget(self.ui_tags)
self.setLayout(main_layout)
# signals
self.ui_tags.doubleClicked.connect(self.tag_double_clicked)
self.ui_search.textChanged.connect(self.search_text_changed)
# init
self.create_model()
def create_model(self):
model = self.ui_tags.model().sourceModel()
self.populate_tree(tags, model.invisibleRootItem())
self.ui_tags.sortByColumn(0, QtCore.Qt.AscendingOrder)
def populate_tree(self, children, parent):
for child in sorted(children):
node = QtGui.QStandardItem(child)
parent.appendRow(node)
if isinstance(children, dict):
self.populate_tree(children[child], node)
def tag_double_clicked(self, item):
text = item.data(role=QtCore.Qt.DisplayRole)
print [text]
self.clickedTag.emit([text])
def search_text_changed(self, text=None):
regExp = QtCore.QRegExp(self.ui_search.text(), QtCore.Qt.CaseInsensitive, QtCore.QRegExp.FixedString)
self.tags_model.text = self.ui_search.text().lower()
self.tags_model.setFilterRegExp(regExp)
if len(self.ui_search.text()) >= 1 and self.tags_model.rowCount() > 0:
self.ui_tags.expandAll()
else:
self.ui_tags.collapseAll()
def main():
app = QtGui.QApplication(sys.argv)
ex = TagsBrowserWidget()
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
答案 0 :(得分:1)
在所有中设置filter-proxy 的区分大小写没有意义,因为您通过覆盖filterAcceptsRow
来绕过内置过滤。即使你没有这样做,setFilterRegExp无论如何都会忽略当前的情况敏感设置。
我会将filter-proxy简化为:
class SearchProxyModel(QtGui.QSortFilterProxyModel):
def setFilterRegExp(self, pattern):
if isinstance(pattern, str):
pattern = QtCore.QRegExp(
pattern, QtCore.Qt.CaseInsensitive,
QtCore.QRegExp.FixedString)
super(SearchProxyModel, self).setFilterRegExp(pattern)
def _accept_index(self, idx):
if idx.isValid():
text = idx.data(QtCore.Qt.DisplayRole)
if self.filterRegExp().indexIn(text) >= 0:
return True
for row in range(idx.model().rowCount(idx)):
if self._accept_index(idx.model().index(row, 0, idx)):
return True
return False
def filterAcceptsRow(self, sourceRow, sourceParent):
idx = self.sourceModel().index(sourceRow, 0, sourceParent)
return self._accept_index(idx)
并将搜索方法更改为:
def search_text_changed(self, text=None):
self.tags_model.setFilterRegExp(self.ui_search.text())
if len(self.ui_search.text()) >= 1 and self.tags_model.rowCount() > 0:
self.ui_tags.expandAll()
else:
self.ui_tags.collapseAll()
现在,SearchProxyModel
对决定如何通过setFilterRegExp
方法执行搜索负有唯一的责任。区分大小写是透明处理的,因此无需预处理输入。
获取后代列表的方法可以这样写:
def tag_double_clicked(self, idx):
text = []
while idx.isValid():
text.append(idx.data(QtCore.Qt.DisplayRole))
idx = idx.parent()
text.reverse()
self.clickedTag.emit(text)