QTableView结合QSortFilterProxyModel仅显示名称列问题求助
问题:QTableView中年龄列无数据显示
我实现了一个存储姓名和年龄数据的QAbstractListModel,需要在QTableView中展示数据并基于年龄过滤,因此使用了QSortFilterProxyModel。但目前只有名称列能正常填充数据,年龄列没有内容,求解决建议。

代码文件
main_window.h
#ifndef MAIN_WINDOW_H #define MAIN_WINDOW_H #include <QMainWindow> #include <QAbstractListModel> #include <QSortFilterProxyModel> namespace Ui { class MainWindow; } // namespace Ui class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget* parent = nullptr); ~MainWindow() override; private: Ui::MainWindow* ui; }; class Data { public: Data(const QString& name, int age); QString name() const; void setName(const QString& name); int age() const; void setAge(int age); private: QString m_name; int m_age; }; class ListModel : public QAbstractListModel { Q_OBJECT public: enum Role { NameRole = Qt::UserRole + 1, AgeRole }; Q_ENUM(Role) explicit ListModel(QObject* prnt = nullptr); int rowCount(const QModelIndex& parent) const override; QVariant data(const QModelIndex& index, int role) const override; private: QList<Data> m_data; }; class FilterModel : public QSortFilterProxyModel { Q_OBJECT public: explicit FilterModel(QObject* prnt = nullptr); int columnCount(const QModelIndex& parent) const override; QVariant headerData(int section, Qt::Orientation orientation, int role) const override; QVariant data(const QModelIndex& index, int role) const override; // QSortFilterProxyModel interface protected: bool filterAcceptsRow(int srcRow, const QModelIndex& srcPrnt) const override; }; #endif // MAIN_WINDOW_H
main_window.cpp
#include "main_window.h" #include "ui_main_window.h" #include <QDebug> MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); auto filterModel = new FilterModel(this); ui->tableView->setModel(filterModel); auto listModel = new ListModel(this); filterModel->setSourceModel(listModel); } MainWindow::~MainWindow() { delete ui; } // ===================================== LIST MODEL ======================================================== ListModel::ListModel(QObject* prnt) : QAbstractListModel (prnt), m_data({{"Bob", 30}, {"Carl", 25}, {"Foo", 20}, {"Mike", 33}, {"Lisa", 35}}) { } int ListModel::rowCount(const QModelIndex& parent) const { return m_data.count(); } QVariant ListModel::data(const QModelIndex& index, int role) const { if (index.row() < rowCount({}) && role > Qt::UserRole) { const auto& item = m_data.at(index.row()); switch (role) { case NameRole: return item.name(); case AgeRole: return item.age(); } } return {}; } // ===================================== FILTER MODEL ======================================================== FilterModel::FilterModel(QObject* prnt) : QSortFilterProxyModel (prnt) { } int FilterModel::columnCount(const QModelIndex& parent) const { return 2; } QVariant FilterModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { return section == 0 ? "Name" : "Age"; } return QSortFilterProxyModel::headerData(section, orientation, role); } QVariant FilterModel::data(const QModelIndex& index, int role) const { if (role == Qt::DisplayRole) { auto srcIdx = mapToSource(index); qInfo() << index.row() << index.column() << index << srcIdx; auto role = index.column() == 0 ? ListModel::NameRole : ListModel::AgeRole; return sourceModel()->data(srcIdx, role); } return QSortFilterProxyModel::data(index, role); } bool FilterModel::filterAcceptsRow(int srcRow, const QModelIndex& srcPrnt) const { Q_UNUSED(srcPrnt); const QModelIndex modelIdx(createIndex(srcRow, 0)); if (modelIdx.isValid()) { QVariant srcData(sourceModel()->data(modelIdx, ListModel::AgeRole)); qInfo() << "srcRow:" << srcRow << ", srcPrnt:" << srcPrnt << srcData; if (srcData.isValid()) { auto age = srcData.toInt(); return age >= 30 && age <= 39; } } return true; } // ===================================== DATA ======================================================== Data::Data(const QString& name, int id) : m_name(name), m_age(id) { } QString Data::name() const { return m_name; } void Data::setName(const QString& name) { m_name = name; } int Data::age() const { return m_age; } void Data::setAge(int id) { m_age = id; }
main_window.ui
<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>MainWindow</class> <widget class="QMainWindow" name="MainWindow"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>860</width> <height>490</height> </rect> </property> <property name="windowTitle"> <string>MainWindow</string> </property> <widget class="QWidget" name="centralWidget"> <widget class="QTableView" name="tableView"> <property name="geometry"> <rect> <x>10</x> <y>10</y> <width>841</width> <height>411</height> </rect> </property> <property name="selectionMode"> <enum>QAbstractItemView::SingleSelection</enum> </property> <property name="selectionBehavior"> <enum>QAbstractItemView::SelectRows</enum> </property> <attribute name="verticalHeaderVisible"> <bool>false</bool> </attribute> </widget> </widget> <widget class="QMenuBar" name="menuBar"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>860</width> <height>24</height> </rect> </property> </widget> <widget class="QToolBar" name="mainToolBar"> <attribute name="toolBarArea"> <enum>TopToolBarArea</enum> </attribute> <attribute name="toolBarBreak"> <bool>false</bool> </attribute> </widget> <widget class="QStatusBar" name="statusBar"/> </widget> <layoutdefault spacing="6" margin="11"/> <resources/> <connections/> </ui>
问题原因及修复方案
1. 源索引映射错误
源模型是QAbstractListModel,仅支持1列(列0)。当代理模型处理年龄列(列1)时,调用mapToSource(index)会得到无效的源索引,因为源模型不存在列1,导致无法获取年龄数据。
修复代码:
修改FilterModel::data方法,直接通过源模型创建列0的索引:
QVariant FilterModel::data(const QModelIndex& index, int role) const { if (role == Qt::DisplayRole) { // 源模型是列表模型,固定使用列0创建索引 QModelIndex srcIdx = sourceModel()->index(index.row(), 0); auto targetRole = index.column() == 0 ? ListModel::NameRole : ListModel::AgeRole; return sourceModel()->data(srcIdx, targetRole); } return QSortFilterProxyModel::data(index, role); }
2. 过滤方法中索引创建错误
filterAcceptsRow中调用createIndex是创建代理模型的索引,而非源模型的索引,导致无法正确获取源数据。
修复代码:
修改FilterModel::filterAcceptsRow方法,通过源模型创建索引:
bool FilterModel::filterAcceptsRow(int srcRow, const QModelIndex& srcPrnt) const { Q_UNUSED(srcPrnt); // 通过源模型创建正确的索引 const QModelIndex modelIdx = sourceModel()->index(srcRow, 0); if (modelIdx.isValid()) { QVariant srcData = sourceModel()->data(modelIdx, ListModel::AgeRole); qInfo() << "srcRow:" << srcRow << ", srcPrnt:" << srcPrnt << srcData; if (srcData.isValid()) { auto age = srcData.toInt(); return age >= 30 && age <= 39; } } return true; }
3. 可选优化:重写roleNames
为了让模型在更多场景(如Qt Quick)下兼容,建议在ListModel中添加roleNames方法:
// ListModel类的public声明 QHash<int, QByteArray> roleNames() const override; // 实现部分 QHash<int, QByteArray> ListModel::roleNames() const { QHash<int, QByteArray> roles; roles[NameRole] = "name"; roles[AgeRole] = "age"; return roles; }
内容的提问来源于stack exchange,提问作者ramtheconqueror
相关产品推荐
相关产品推荐

