Чтобы отфильтровать TreeView с двумя уровнями (см. Изображение ниже), я использовал класс QSortFilterProxyModel, который имеет QStandardItemModel в качестве исходной модели.
, чтобы игнорировать фильтрацию узлов root, я перегружен метод filterAcceptsRow()
выглядит следующим образом.
это мой основной проект, работающий с копированием / вставкой кода ниже класса модели, я заменил импорт данных из базы данных на словарь фиктивных данных
import sys
from PyQt5 import QtWidgets,QtGui
from PyQt5.QtCore import QSortFilterProxyModel
services =[{'id':1,'chanel_name':'France 24','satId':0},
{'id':2,'chanel_name':'Eurosport 02','satId':0},
{'id':3,'chanel_name':'CANAL +','satId':0},
{'id':4,'chanel_name':'Eurosport 02','satId':0},
{'id':5,'chanel_name':'TV 5','satId':0},
{'id':6,'chanel_name':'BBC World','satId':0},
{'id':7,'chanel_name':'M6','satId':0},
{'id':15,'chanel_name':'TEVA','satId':0},
{'id':8,'chanel_name':'PLANETTE','satId':1},
{'id':9,'chanel_name':'France 2','satId':1},
{'id':10,'chanel_name':'France 3','satId':1},
{'id':11,'chanel_name':'MBC 1','satId':1},
{'id':12,'chanel_name':'NIL TV','satId':1},
{'id':13,'chanel_name':'M2','satId':1},
{'id':14,'chanel_name':'CANAL 02','satId':1},
{'id':15,'chanel_name':'CHANEL 1','satId':2},
{'id':8,'chanel_name':'CINE+ FAMILY','satId':2},
{'id':9,'chanel_name':'OCS CITY','satId':2},
{'id':10,'chanel_name':'OCS CHOCS','satId':2},
{'id':11,'chanel_name':'ACTION','satId':2},
{'id':12,'chanel_name':'NATIONAL GEO','satId':2},
{'id':13,'chanel_name':'TVEA','satId':2},
{'id':14,'chanel_name':'CANAL 02','satId':2},
]
class ProxyServiceBySatModel(QSortFilterProxyModel):
def __init__(self,):
super().__init__()
def filterAcceptsRow(self, source_row, source_parent):
index = self.sourceModel().index(source_row,0,source_parent)
if not source_parent.isValid():
return True
else:
return QSortFilterProxyModel.filterAcceptsRow(self,source_row, source_parent)
class ServicesBySat(QtGui.QStandardItemModel):
def __init__(self):
super(ServicesBySat,self).__init__()
self.setHorizontalHeaderLabels(['Sat name'])
self.satellite = ['ASTRA 19° E','HotBird 13°','NILSAT 7° W']
def importData(self, root=None):
sats= {}
root = self.invisibleRootItem()
for service in services:
sat = service['satId']
if sat not in sats:
parent = root
parent.appendRow(QtGui.QStandardItem(self.satellite[sat]))
sats[sat] = parent.child(parent.rowCount() - 1)
else:
parent = sats[sat]
parent.appendRow(QtGui.QStandardItem(service['chanel_name']))
class MainWindowISE(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle('Tree search')
self.setGeometry(100,100,300,300)
self.setupUi()
self.model = ServicesBySat()
self.model.importData()
self.proxyModel = ProxyServiceBySatModel()
self.proxyModel.setSourceModel(self.model)
self.proxyModel.setFilterKeyColumn(-1)
self.treeView_services.setModel(self.proxyModel)
self.lineEdit_Search.textEdited.connect(self.on_lineEditSearch_textEdited)
def setupUi(self):
self.gridLayout = QtWidgets.QGridLayout(self)
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.label = QtWidgets.QLabel("Search : ", self)
self.horizontalLayout.addWidget(self.label)
self.lineEdit_Search = QtWidgets.QLineEdit(self)
self.horizontalLayout.addWidget(self.lineEdit_Search)
self.gridLayout.addLayout(self.horizontalLayout, 0, 0, 1, 1)
self.treeView_services = QtWidgets.QTreeView(self)
self.gridLayout.addWidget(self.treeView_services, 1, 0, 1, 1)
def on_lineEditSearch_textEdited(self):
self.proxyModel.setFilterRegularExpression(self.lineEdit_Search.text())
def main():
app = QtWidgets.QApplication(sys.argv)
window =MainWindowISE()
window.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
фильтрация работает хорошо, но теперь, чтобы улучшить поведение моего фильтра, я хочу скрыть узлы root, которые не имеют дочерних элементов, в фильтрованных элементах, добавив условие, подобное этому, учитывая, что self - мой прокси модель.
Например, как показано в представлении ниже, я не хочу отображать элемент NILESAT, поскольку у него нет дочерних элементов, соответствующих шаблону фильтра
Я изменил свой метод следующим образом, чтобы достичь ожидаемого результата, но безуспешно.
class ProxyServiceBySatModel (QSortFi lterProxyModel): def init (self,): super (). init ()
def filterAcceptsRow(self, source_row, source_parent):
index = self.sourceModel().index(source_row,0,source_parent)
if not source_parent.isValid() and self.hasChildren(index) :
return True
else:
return QSortFilterProxyModel.filterAcceptsRow(self,source_row, source_parent)
или я могу использовать это условие, но получить тот же результат ,
if not source_parent.isValid() and self.rowCount(index) > 0 :
но я получаю исключение
QSortFilterProxyModel: индекс из неверной модели передан в mapToSource
как можно получить текущую строку Подсчет или значение hasChildren внутреннего метода filterAcceptsRow или другой способ достижения ожидаемого результата.