QListView开发入门

时间:2025-03-31 08:49:08

1. QListView 基础介绍

QListView 是 Qt 框架中用于显示项目列表的控件,属于模型/视图架构的一部分。它提供了一种灵活的方式来显示和操作项目列表。

主要特点:

  • 基于模型/视图架构

  • 支持多种视图模式(列表、图标)

  • 内置选择、编辑功能

  • 可自定义项目显示方式

2. 基本使用

2.1 简单示例

#include <QApplication>
#include <QListView>
#include <QStringListModel>

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    
    // 创建数据模型
    QStringList strings;
    strings << "苹果" << "香蕉" << "橙子" << "西瓜";
    QStringListModel model(strings);
    
    // 创建并设置ListView
    QListView listView;
    listView.setModel(&model);
    listView.setWindowTitle("水果列表");
    listView.resize(300, 200);
    listView.show();
    
    return app.exec();
}

2.2 常用方法

// 设置选择模式
listView.setSelectionMode(QAbstractItemView::SingleSelection);  // 单选
listView.setSelectionMode(QAbstractItemView::MultiSelection);   // 多选

// 设置视图模式
listView.setViewMode(QListView::ListMode);    // 列表模式(默认)
listView.setViewMode(QListView::IconMode);    // 图标模式

// 设置是否可编辑
listView.setEditTriggers(QAbstractItemView::NoEditTriggers);    // 不可编辑
listView.setEditTriggers(QAbstractItemView::DoubleClicked);     // 双击编辑

// 设置网格线
listView.setGridSize(QSize(100, 30));         // 设置项目大小
listView.setSpacing(5);                       // 设置项目间距

 

3. 数据模型使用

3.1 使用 QStringListModel

QStringListModel *model = new QStringListModel(this);
QStringList list;
list << "项目1" << "项目2" << "项目3";
model->setStringList(list);

ui->listView->setModel(model);

3.2 使用 QStandardItemModel

QStandardItemModel *model = new QStandardItemModel(this);

// 添加文本项目
QStandardItem *item1 = new QStandardItem("文本项目");
model->appendRow(item1);

// 添加带图标的项目
QStandardItem *item2 = new QStandardItem(QIcon(":/icon.png"), "带图标项目");
model->appendRow(item2);

// 添加可勾选项目
QStandardItem *item3 = new QStandardItem("可勾选项目");
item3->setCheckable(true);
item3->setCheckState(Qt::Checked);
model->appendRow(item3);

ui->listView->setModel(model);

 

4. 项目选择与操作

4.1 获取选中项目

// 获取当前选中项的索引
QModelIndex currentIndex = ui->listView->currentIndex();

// 获取所有选中项的索引列表
QModelIndexList selectedIndexes = ui->listView->selectionModel()->selectedIndexes();

// 通过模型获取数据
QVariant data = model->data(currentIndex, Qt::DisplayRole);

4.2 添加和删除项目

// 添加项目
void addItem(const QString &text) {
    QStandardItemModel *model = qobject_cast<QStandardItemModel*>(ui->listView->model());
    if(model) {
        model->appendRow(new QStandardItem(text));
    }
}

// 删除选中项目
void removeSelectedItems() {
    QStandardItemModel *model = qobject_cast<QStandardItemModel*>(ui->listView->model());
    if(!model) return;
    
    QModelIndexList selected = ui->listView->selectionModel()->selectedIndexes();
    foreach(QModelIndex index, selected) {
        model->removeRow(index.row());
    }
}

5. 自定义显示

5.1 使用委托(Delegate)自定义项目显示

class CustomDelegate : public QStyledItemDelegate {
public:
    using QStyledItemDelegate::QStyledItemDelegate;
    
    void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override {
        if (option.state & QStyle::State_Selected) {
            painter->fillRect(option.rect, QColor("#4CAF50"));  // 选中项背景色
        }
        
        QStyledItemDelegate::paint(painter, option, index);
    }
    
    QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override {
        // 设置项目高度
        QSize size = QStyledItemDelegate::sizeHint(option, index);
        size.setHeight(40);
        return size;
    }
};

// 使用自定义委托
ui->listView->setItemDelegate(new CustomDelegate(this));

 5.2 设置交替行颜色

ui->listView->setAlternatingRowColors(true);
ui->listView->setStyleSheet("alternate-background-color: #f0f0f0;");

6. 自定义模型

在 Qt 中创建自定义模型需要继承自 QAbstractItemModel 或其子类(如 QAbstractListModel)。对于列表视图,通常继承 QAbstractListModel 更为简单。

1).基本模型结构

#include <QAbstractListModel>
#include <QStringList>

class CustomListModel : public QAbstractListModel {
    Q_OBJECT
    
public:
    explicit CustomListModel(QObject *parent = nullptr);
    
    // 必须重写的基类方法
    int rowCount(const QModelIndex &parent = QModelIndex()) const override;
    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
    
    // 可选重写的方法
    QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
    Qt::ItemFlags flags(const QModelIndex &index) const override;
    
    // 自定义数据操作方法
    void addItem(const QString &item);
    void removeItem(int row);
    
private:
    QStringList m_data; // 实际存储数据的容器
};

2). 实现自定义模型

2.1 基本实现
CustomListModel::CustomListModel(QObject *parent) 
    : QAbstractListModel(parent) {}

int CustomListModel::rowCount(const QModelIndex &parent) const {
    // 对于列表模型,parent无效时应返回项目数
    if (parent.isValid())
        return 0;
    return m_data.size();
}

QVariant CustomListModel::data(const QModelIndex &index, int role) const {
    if (!index.isValid() || index.row() >= m_data.size())
        return QVariant();
    
    switch (role) {
    case Qt::DisplayRole:
    case Qt::EditRole:
        return m_data.at(index.row());
    case Qt::ToolTipRole:
        return QString("项目: %1").arg(m_data.at(index.row()));
    case Qt::TextAlignmentRole:
        return Qt::AlignVCenter | Qt::AlignLeft;
    default:
        return QVariant();
    }
}

Qt::ItemFlags CustomListModel::flags(const QModelIndex &index) const {
    if (!index.isValid())
        return Qt::NoItemFlags;
    
    return QAbstractListModel::flags(index) | Qt::ItemIsEditable | Qt::ItemIsSelectable;
}
2.2 数据操作方法
void CustomListModel::addItem(const QString &item) {
    beginInsertRows(QModelIndex(), m_data.size(), m_data.size());
    m_data.append(item);
    endInsertRows();
}

void CustomListModel::removeItem(int row) {
    if (row < 0 || row >= m_data.size())
        return;
    
    beginRemoveRows(QModelIndex(), row, row);
    m_data.removeAt(row);
    endRemoveRows();
}

bool CustomListModel::setData(const QModelIndex &index, const QVariant &value, int role) {
    if (!index.isValid() || role != Qt::EditRole)
        return false;
    
    m_data.replace(index.row(), value.toString());
    emit dataChanged(index, index, {role});
    return true;
}

3). 高级自定义功能

3.1 支持拖放操作
// 在构造函数中添加
setSupportedDragActions(Qt::MoveAction);
setSupportedDropActions(Qt::MoveAction);

Qt::DropActions CustomListModel::supportedDropActions() const {
    return Qt::MoveAction;
}

QStringList CustomListModel::mimeTypes() const {
    return {"application/vnd.text.list"};
}

QMimeData *CustomListModel::mimeData(const QModelIndexList &indexes) const {
    auto *mimeData = new QMimeData;
    QByteArray encodedData;
    
    QDataStream stream(&encodedData, QIODevice::WriteOnly);
    for (const QModelIndex &index : indexes) {
        if (index.isValid())
            stream << m_data.at(index.row());
    }
    
    mimeData->setData("application/vnd.text.list", encodedData);
    return mimeData;
}

bool CustomListModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
                                 int row, int column, const QModelIndex &parent) {
    if (!data->hasFormat("application/vnd.text.list"))
        return false;
    
    QByteArray encodedData = data->data("application/vnd.text.list");
    QDataStream stream(&encodedData, QIODevice::ReadOnly);
    QStringList newItems;
    
    while (!stream.atEnd()) {
        QString text;
        stream >> text;
        newItems << text;
    }
    
    // 插入新项目
    int beginRow = row != -1 ? row : rowCount(parent);
    for (const QString &text : qAsConst(newItems)) {
        insertRow(beginRow);
        setData(index(beginRow, 0, parent), text);
        beginRow++;
    }
    
    return true;
}
3.2 自定义角色
// 在头文件中定义自定义角色
enum CustomRoles {
    BackgroundColorRole = Qt::UserRole + 1,
    TextColorRole,
    IconRole
};

// 在data()方法中添加处理
QVariant CustomListModel::data(const QModelIndex &index, int role) const {
    // ... 其他代码
    
    switch (role) {
    case BackgroundColorRole:
        return index.row() % 2 == 0 ? QColor("#f0f0f0") : QColor("#ffffff");
    case TextColorRole:
        return QColor("#333333");
    case IconRole:
        return QIcon(":/icons/item.png");
    // ... 其他角色
    }
}

4). 使用自定义模型 

4.1 基本使用
CustomListModel *model = new CustomListModel(this);
model->addItem("项目1");
model->addItem("项目2");
model->addItem("项目3");

QListView *listView = new QListView;
listView->setModel(model);

// 启用拖放
listView->setDragEnabled(true);
listView->setAcceptDrops(true);
listView->setDropIndicatorShown(true);
listView->setDragDropMode(QAbstractItemView::InternalMove);
4.2 使用自定义角色
// 使用委托显示自定义角色
class CustomDelegate : public QStyledItemDelegate {
public:
    using QStyledItemDelegate::QStyledItemDelegate;
    
    void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override {
        QStyleOptionViewItem opt = option;
        initStyleOption(&opt, index);
        
        // 获取自定义角色数据
        QColor bgColor = index.data(BackgroundColorRole).value<QColor>();
        QColor textColor = index.data(TextColorRole).value<QColor>();
        QIcon icon = index.data(IconRole).value<QIcon>();
        
        // 自定义绘制
        painter->fillRect(opt.rect, bgColor);
        
        QRect iconRect = opt.rect.adjusted(5, 5, -5, -5);
        iconRect.setWidth(32);
        icon.paint(painter, iconRect);
        
        QRect textRect = opt.rect.adjusted(42, 0, -5, 0);
        painter->setPen(textColor);
        painter->drawText(textRect, Qt::AlignVCenter | Qt::AlignLeft, opt.text);
    }
    
    QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override {
        return QSize(200, 40);
    }
};

// 设置自定义委托
listView->setItemDelegate(new CustomDelegate);

5). 性能优化技巧

1)实现 canFetchMore/fetchMore 用于大数据集的懒加载:

bool canFetchMore(const QModelIndex &parent) const override {
    return !m_allDataLoaded && m_data.size() < m_totalItems;
}

void fetchMore(const QModelIndex &parent) override {
    int remaining = m_totalItems - m_data.size();
    int itemsToFetch = qMin(100, remaining);
    
    if (itemsToFetch <= 0) {
        m_allDataLoaded = true;
        return;
    }
    
    beginInsertRows(QModelIndex(), m_data.size(), m_data.size() + itemsToFetch - 1);
    // 加载更多数据...
    endInsertRows();
}

2)优化 data() 方法:避免在 data() 中进行复杂计算
3)批量更新:使用 beginResetModel()/endResetModel() 进行大批量数据更新
4)使用模型测试:实现 QAbstractItemModelTester 检查模型一致性 

7. 信号与槽 

// 当前项变化信号
connect(ui->listView, &QListView::clicked, [](const QModelIndex &index){
    qDebug() << "点击了:" << index.data().toString();
});

// 双击项目信号
connect(ui->listView, &QListView::doubleClicked, [](const QModelIndex &index){
    qDebug() << "双击了:" << index.data().toString();
});

// 选择变化信号
connect(ui->listView->selectionModel(), &QItemSelectionModel::selectionChanged, 
    [](const QItemSelection &selected, const QItemSelection &deselected){
        qDebug() << "选择已改变";
    });

8. 高级功能

7.1 拖放支持
// 启用拖放
ui->listView->setDragEnabled(true);          // 允许拖动
ui->listView->setAcceptDrops(true);          // 接受放置
ui->listView->setDropIndicatorShown(true);   // 显示放置指示器
ui->listView->setDragDropMode(QAbstractItemView::InternalMove);  // 内部移动

// 自定义拖放行为需要重写模型的 mimeData() 和 dropMimeData() 方法
7.2 排序和过滤
// 启用排序
ui->listView->setSortingEnabled(true);

// 使用代理模型进行过滤
QSortFilterProxyModel *proxyModel = new QSortFilterProxyModel(this);
proxyModel->setSourceModel(sourceModel);
proxyModel->setFilterRegExp(QRegExp("pattern", Qt::CaseInsensitive));
ui->listView->setModel(proxyModel);

9. 性能优化

  1. 大数据集处理

    // 对于大数据集,禁用不必要的功能
    ui->listView->setUniformItemSizes(true);  // 所有项目大小相同可提高性能
    ui->listView->setViewMode(QListView::ListMode);  // 列表模式比图标模式性能更好
  2. 自定义模型:对于复杂数据,考虑实现自定义模型,只加载可见项数据

  3. 避免频繁更新:批量更新数据而不是逐项更新