Qt 模型/视图架构 #
架构概述 #
text
┌─────────────────────────────────────────────────────────────┐
│ Model-View-Delegate 架构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Model │────>│ View │<────│ Delegate │ │
│ │ (数据) │ │ (显示) │ │ (编辑) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │ │ │ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 数据流 │ │
│ │ Model 提供数据 → View 显示数据 → Delegate 编辑数据 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 优势: │
│ ✅ 数据与显示分离 │
│ ✅ 同一数据多种视图 │
│ ✅ 自定义显示和编辑 │
│ │
└─────────────────────────────────────────────────────────────┘
预定义模型 #
QStringListModel #
cpp
#include <QStringListModel>
#include <QListView>
QStringList data = {"Item 1", "Item 2", "Item 3", "Item 4"};
QStringListModel *model = new QStringListModel(data, this);
QListView *listView = new QListView(this);
listView->setModel(model);
// 修改数据
model->setStringList({"New 1", "New 2", "New 3"});
// 添加数据
model->insertRow(model->rowCount());
QModelIndex index = model->index(model->rowCount() - 1);
model->setData(index, "New Item");
QFileSystemModel #
cpp
#include <QFileSystemModel>
#include <QTreeView>
QFileSystemModel *model = new QFileSystemModel(this);
model->setRootPath(QDir::rootPath());
QTreeView *treeView = new QTreeView(this);
treeView->setModel(model);
treeView->setRootIndex(model->index(QDir::homePath()));
// 设置过滤器
model->setFilter(QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot);
// 设置名称过滤器
model->setNameFilters(QStringList() << "*.txt" << "*.cpp");
model->setNameFilterDisables(false); // 隐藏不匹配的文件
QStandardItemModel #
cpp
#include <QStandardItemModel>
#include <QTableView>
QStandardItemModel *model = new QStandardItemModel(4, 3, this);
model->setHorizontalHeaderLabels({"Name", "Age", "City"});
// 设置数据
model->setItem(0, 0, new QStandardItem("Alice"));
model->setItem(0, 1, new QStandardItem("25"));
model->setItem(0, 2, new QStandardItem("Beijing"));
model->setItem(1, 0, new QStandardItem("Bob"));
model->setItem(1, 1, new QStandardItem("30"));
model->setItem(1, 2, new QStandardItem("Shanghai"));
QTableView *tableView = new QTableView(this);
tableView->setModel(model);
// 添加行
QList<QStandardItem*> rowItems;
rowItems << new QStandardItem("Charlie")
<< new QStandardItem("28")
<< new QStandardItem("Guangzhou");
model->appendRow(rowItems);
// 设置项属性
QStandardItem *item = model->item(0, 0);
item->setEditable(true);
item->setCheckable(true);
item->setCheckState(Qt::Checked);
item->setIcon(QIcon(":/icon.png"));
item->setForeground(Qt::red);
自定义模型 #
只读模型 #
cpp
class MyModel : public QAbstractTableModel
{
Q_OBJECT
public:
MyModel(QObject *parent = nullptr) : QAbstractTableModel(parent) {}
// 必须实现的虚函数
int rowCount(const QModelIndex &parent = QModelIndex()) const override
{
if (parent.isValid()) return 0;
return m_data.size();
}
int columnCount(const QModelIndex &parent = QModelIndex()) const override
{
if (parent.isValid()) return 0;
return 3; // 3 列
}
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
{
if (!index.isValid() || index.row() >= m_data.size()) {
return QVariant();
}
if (role == Qt::DisplayRole || role == Qt::EditRole) {
const auto &row = m_data.at(index.row());
switch (index.column()) {
case 0: return row.name;
case 1: return row.age;
case 2: return row.city;
}
}
if (role == Qt::TextAlignmentRole) {
if (index.column() == 1) {
return Qt::AlignCenter;
}
}
return QVariant();
}
// 表头
QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const override
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
switch (section) {
case 0: return "Name";
case 1: return "Age";
case 2: return "City";
}
}
return QVariant();
}
private:
struct DataRow {
QString name;
int age;
QString city;
};
QList<DataRow> m_data;
};
可编辑模型 #
cpp
class EditableModel : public QAbstractTableModel
{
Q_OBJECT
public:
using QAbstractTableModel::QAbstractTableModel;
Qt::ItemFlags flags(const QModelIndex &index) const override
{
if (!index.isValid()) {
return Qt::NoItemFlags;
}
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
}
bool setData(const QModelIndex &index, const QVariant &value,
int role = Qt::EditRole) override
{
if (!index.isValid() || role != Qt::EditRole) {
return false;
}
// 更新数据
// m_data[index.row()][index.column()] = value;
emit dataChanged(index, index, {role});
return true;
}
bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override
{
if (parent.isValid()) return false;
beginInsertRows(parent, row, row + count - 1);
// 插入数据
for (int i = 0; i < count; ++i) {
// m_data.insert(row, DataRow{});
}
endInsertRows();
return true;
}
bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override
{
if (parent.isValid()) return false;
beginRemoveRows(parent, row, row + count - 1);
// 删除数据
for (int i = 0; i < count; ++i) {
// m_data.removeAt(row);
}
endRemoveRows();
return true;
}
};
视图类 #
QListView #
cpp
QListView *listView = new QListView(this);
listView->setModel(model);
// 显示模式
listView->setViewMode(QListView::ListMode); // 列表模式
listView->setViewMode(QListView::IconMode); // 图标模式
// 选择模式
listView->setSelectionMode(QAbstractItemView::SingleSelection);
listView->setSelectionMode(QAbstractItemView::ExtendedSelection);
// 编辑触发
listView->setEditTriggers(QAbstractItemView::DoubleClicked);
// 信号
connect(listView, &QListView::clicked, [](const QModelIndex &index) {
qDebug() << "Clicked:" << index.row();
});
connect(listView->selectionModel(), &QItemSelectionModel::selectionChanged,
[](const QItemSelection &selected, const QItemSelection &deselected) {
qDebug() << "Selection changed";
});
QTableView #
cpp
QTableView *tableView = new QTableView(this);
tableView->setModel(model);
// 列宽设置
tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive);
tableView->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
tableView->setColumnWidth(1, 100);
// 行高设置
tableView->verticalHeader()->setDefaultSectionSize(30);
// 隐藏表头
tableView->horizontalHeader()->hide();
tableView->verticalHeader()->hide();
// 交替行颜色
tableView->setAlternatingRowColors(true);
// 排序
tableView->setSortingEnabled(true);
// 选择行为
tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
QTreeView #
cpp
QTreeView *treeView = new QTreeView(this);
treeView->setModel(model);
// 展开/折叠
treeView->expandAll();
treeView->collapseAll();
treeView->expandToDepth(2);
// 设置展开
treeView->setExpanded(index, true);
// 信号
connect(treeView, &QTreeView::expanded, [](const QModelIndex &index) {
qDebug() << "Expanded:" << index;
});
connect(treeView, &QTreeView::collapsed, [](const QModelIndex &index) {
qDebug() << "Collapsed:" << index;
});
代理类 (Delegate) #
自定义代理 #
cpp
class SpinBoxDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
SpinBoxDelegate(QObject *parent = nullptr) : QStyledItemDelegate(parent) {}
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &,
const QModelIndex &) const override
{
QSpinBox *editor = new QSpinBox(parent);
editor->setFrame(false);
editor->setMinimum(0);
editor->setMaximum(100);
return editor;
}
void setEditorData(QWidget *editor, const QModelIndex &index) const override
{
int value = index.model()->data(index, Qt::EditRole).toInt();
QSpinBox *spinBox = qobject_cast<QSpinBox*>(editor);
spinBox->setValue(value);
}
void setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const override
{
QSpinBox *spinBox = qobject_cast<QSpinBox*>(editor);
spinBox->interpretText();
int value = spinBox->value();
model->setData(index, value, Qt::EditRole);
}
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option,
const QModelIndex &) const override
{
editor->setGeometry(option.rect);
}
};
// 使用代理
tableView->setItemDelegateForColumn(1, new SpinBoxDelegate(this));
选择模型 #
cpp
// 获取选择模型
QItemSelectionModel *selectionModel = tableView->selectionModel();
// 获取选中的索引
QModelIndexList selected = selectionModel->selectedIndexes();
QModelIndexList selectedRows = selectionModel->selectedRows();
QModelIndexList selectedColumns = selectionModel->selectedColumns();
// 设置选中
selectionModel->select(index, QItemSelectionModel::Select);
selectionModel->select(index, QItemSelectionModel::Toggle);
selectionModel->select(index, QItemSelectionModel::ClearAndSelect);
// 清除选择
selectionModel->clearSelection();
// 设置当前索引
selectionModel->setCurrentIndex(index, QItemSelectionModel::SelectCurrent);
// 信号
connect(selectionModel, &QItemSelectionModel::selectionChanged,
[](const QItemSelection &selected, const QItemSelection &deselected) {
for (const QModelIndex &index : selected.indexes()) {
qDebug() << "Selected:" << index;
}
});
下一步 #
现在你已经掌握了模型/视图架构,接下来学习 图形视图框架,了解 Qt 的 2D 图形编程!
最后更新:2026-03-29