Вопрос или проблема
В проекте Qt 6.8, использующем QML для фронтенда и Python для бэкенда, я пытаюсь реализовать систему фильтров для TableModel/ListModel. Я прочитал документацию по QSortFilterProxyModel
, но не нашел работающий пример на Python.
Как я мог бы модифицировать упрощенный код ниже, чтобы интегрировать метод фильтрации модели через бэкенд моего приложения и связать его с панелью поиска?
Вот мой текущий код:
main.py:
import sys
from pathlib import Path
from PySide6.QtCore import QtMsgType, qInstallMessageHandler
from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine
def handle_qml_message(msg_type, context, message):
print(f"QML: {message}")
if __name__ == "__main__":
qInstallMessageHandler(handle_qml_message)
app = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine()
qml_file = Path(__file__).resolve().parent / "main.qml"
engine.load(qml_file)
if not engine.rootObjects():
sys.exit(-1)
sys.exit(app.exec())
main.qml:
import QtQuick
import QtQuick.Window
import Qt.labs.qmlmodels
import QtQuick.Controls
Window {
width: 640
height: 480
visible: true
TextField {
id: textField
anchors {
top: parent.top
left: parent.left
}
width: 300
height: 30
placeholderText: "Поиск ..."
onTextChanged: print(text)
}
HorizontalHeaderView {
id: horizontalHeader
z: 2
syncView: tableView
anchors {
top: textField.bottom
left: parent.left
}
resizableColumns: false
model: ["Имя", "Цвет"]
delegate: Rectangle {
implicitHeight: 50
implicitWidth: 50
color: "lightblue"
Text {
anchors.centerIn: parent
text: modelData
}
}
}
TableView {
id: tableView
anchors {
top: horizontalHeader.bottom
bottom: parent.bottom
left: parent.left
right: parent.right
}
columnSpacing: 1
rowSpacing: 1
clip: true
model: TableModel {
TableModelColumn { display: "name" }
TableModelColumn { display: "color" }
rows: [
{ "name": "кот", "color": "черный" },
{ "name": "собака", "color": "коричневый" },
{ "name": "птица", "color": "белый" },
{ "name": "рыба", "color": "синий" },
{ "name": "кролик", "color": "серый" },
{ "name": "лошадь", "color": "коричневый" },
{ "name": "змея", "color": "зеленый" },
{ "name": "хомяк", "color": "золотистый" },
{ "name": "черепаха", "color": "зеленый" },
{ "name": "попугай", "color": "красный" },
{ "name": "лягушка", "color": "зеленый" },
{ "name": "ящерица", "color": "коричневый" },
{ "name": "бабочка", "color": "многоцветный" },
{ "name": "пчела", "color": "желтый" },
{ "name": "овца", "color": "белый" },
{ "name": "коза", "color": "черный" },
{ "name": "свинья", "color": "розовый" },
{ "name": "корова", "color": "черно-белый" },
{ "name": "утка", "color": "желтый" },
{ "name": "лебедь", "color": "белый" },
{ "name": "орел", "color": "коричневый" },
{ "name": "сова", "color": "серый" },
{ "name": "павлин", "color": "синий и зеленый" },
{ "name": "олень", "color": "коричневый" },
{ "name": "медведь", "color": "темно-коричневый" },
{ "name": "волк", "color": "серый" },
{ "name": "лис", "color": "оранжевый" },
{ "name": "лев", "color": "золотистый" },
{ "name": "тигр", "color": "оранжевый с полосами" },
{ "name": "слон", "color": "серый" }
]
}
delegate: Rectangle {
implicitWidth: 100
implicitHeight: 50
border.width: 1
Text {
text: display
anchors.centerIn: parent
}
}
}
}
Ответ или решение
Для реализации фильтрации в TableModel
вашего проекта на Qt 6.8 с использованием QML для фронтенда и Python для бэкенда, мы можем воспользоваться классом QSortFilterProxyModel
, который позволяет применять фильтрацию и сортировку к данным вашего модели. В данной инструкции я покажу, как интегрировать механизм фильтрации, связывая его с текстовым полем поиска в вашем интерфейсе.
1. Изменяем Python-бэкенд
Сначала нам нужно создать класс, который будет представлять собой QSortFilterProxyModel
и связывать его с вашим исходным TableModel
. Этот класс будет отвечать за фильтрацию данных на основе введенного текста.
import sys
from pathlib import Path
from PySide6.QtCore import Qt, QSortFilterProxyModel, QAbstractTableModel, QModelIndex
from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine
# Исходная модель данных
class AnimalModel(QAbstractTableModel):
def __init__(self, animals=None):
super().__init__()
self._animals = animals or []
def rowCount(self, parent=QModelIndex()):
return len(self._animals)
def columnCount(self, parent=QModelIndex()):
return 2 # Две колонки: name и color
def data(self, index, role=Qt.DisplayRole):
if role == Qt.DisplayRole:
animal = self._animals[index.row()]
return animal['name'] if index.column() == 0 else animal['color']
return None
def get_animals(self):
return self._animals
class AnimalFilterModel(QSortFilterProxyModel):
def __init__(self, parent=None):
super().__init__(parent=parent)
self.source_model = None
def setSourceModel(self, model):
super().setSourceModel(model)
def filterRegExp(self):
return super().filterRegExp()
def setFilterRegExp(self, pattern):
super().setFilterRegExp(pattern)
# Основной блок приложения
if __name__ == "__main__":
app = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine()
animal_list = [
{"name": "cat", "color": "black"},
{"name": "dog", "color": "brown"},
# ... и другие животные
]
model = AnimalModel(animal_list)
filter_model = AnimalFilterModel()
filter_model.setSourceModel(model)
engine.rootContext().setContextProperty("filterModel", filter_model)
qml_file = Path(__file__).resolve().parent / "main.qml"
engine.load(qml_file)
if not engine.rootObjects():
sys.exit(-1)
sys.exit(app.exec())
2. Обновляем QML интерфейс
Теперь адаптируем QML для использования нашего filterModel
. Мы добавим функциональность, чтобы связывать поле ввода с фильтром.
import QtQuick
import QtQuick.Window
import QtQml.Models
Window {
width: 640
height: 480
visible: true
TextField {
id: textField
placeholderText: "Поиск ..."
anchors {
top: parent.top
left: parent.left
}
width: 300
height: 30
onTextChanged: {
filterModel.setFilterRegExp(textField.text);
}
}
TableView {
id: tableView
anchors {
top: textField.bottom
bottom: parent.bottom
left: parent.left
right: parent.right
}
model: filterModel
columnSpacing: 1
rowSpacing: 1
delegate: Rectangle {
implicitWidth: 100
implicitHeight: 50
border.width: 1
Text {
text: model.name + " - " + model.color
anchors.centerIn: parent
}
}
}
}
Заключение
Теперь у вас есть рабочий пример фильтрации TableModel
с использованием QSortFilterProxyModel
на Python и QML. Ввод текста в поле поиска будет автоматически обновлять отображаемые данные в TableView
. Эта реализация позволила отделить логику фильтрации от представления, что делает код более чистым и простым для понимания.
Если у вас возникнут дополнительные вопросы или вам потребуется помощь с другими аспектами вашего проекта, не стесняйтесь задавать их.
Спасибо за информативную статью! Я хотел бы поделиться альтернативным решением вашей задачи по фильтрации данных в
TableModel
без необходимости расширять бэкенд на Python.Вместо использования
QSortFilterProxyModel
на стороне Python, вы можете воспользоваться мощными возможностями JavaScript и QML для реализации фильтрации непосредственно в QML. Это может значительно упростить ваш код и сделать его более гибким.Вот как можно изменить ваш файл
main.qml
:import QtQuick import QtQuick.Window import Qt.labs.qmlmodels import QtQuick.Controls Window { width: 640 height: 480 visible: true property string filterText: "" TextField { id: textField anchors { top: parent.top left: parent.left } width: 300 height: 30 placeholderText: "Поиск ..." onTextChanged: filterText = text.toLowerCase() } ListModel { id: animalModel ListElement { name: "кот"; color: "черный" } ListElement { name: "собака"; color: "коричневый" } // ... остальные элементы } ListView { id: listView anchors { top: textField.bottom bottom: parent.bottom left: parent.left right: parent.right } model: ListModel { id: filteredModel dynamicRoles: true function updateFilter() { clear() for (var i = 0; i < animalModel.count; ++i) { var item = animalModel.get(i) if (item.name.toLowerCase().indexOf(filterText) !== -1 || item.color.toLowerCase().indexOf(filterText) !== -1) { append(item) } } } Component.onCompleted: updateFilter() } delegate: Rectangle { implicitWidth: parent.width height: 50 border.width: 1 Text { text: name + " - " + color anchors.centerIn: parent } } } Connections { target: textField onTextChanged: filteredModel.updateFilter() } }
В этом примере мы используем
ListModel
для хранения данных и динамически обновляем фильтрованный список при изменении текста в поле поиска. Это позволяет избежать лишней сложности и сохранить всю логику на стороне QML.Однако, если ваш проект предполагает работу с большими объемами данных или требует более сложной логики фильтрации, использование
QSortFilterProxyModel
может быть более оправданным. В этом случае, я бы рекомендовал обратить внимание на библиотеку PyQt5, которая предоставляет более подробную документацию и примеры для Python.Также стоит отметить, что при использовании
QSortFilterProxyModel
на стороне Python, вы можете переопределить методfilterAcceptsRow
, чтобы задать кастомную логику фильтрации, что сделает ваш фильтр более гибким и эффективным.Надеюсь, это поможет вам найти оптимальное решение для вашего проекта!