Qt Model/View

向ai提问:

我想要你写一个Qt程序,使用model和view: view使用QTableView即可,我需要使用model存储4个信息,命令码,发送方地址,接收方地址,报文内容,你可以推荐下使用什么model吗?

方案一:QStandardItemModel(最推荐,上手最快)

这是Qt提供的一个通用模型,使用起来非常方便,适合大多数常规场景。

  • 特点 :它将数据和模型合并在一个类中,通过QStandardItem对象来存储每个单元格的数据。你可以像操作二维数组一样操作它。

  • 优点:API直观,易于理解和使用,适合快速开发。

  • 缺点 :每个单元格都是一个独立的对象,当数据量极大(例如数十万行)时,内存和性能开销会变大。

方案二:继承 QAbstractTableModel(灵活性最高)

如果你的数据来源于自定义的数据结构(例如一个结构体列表),或者需要进行复杂的数据处理,可以选择继承**QAbstractTableModel**来实现自定义模型。

  • 特点:模型本身不存储数据,只作为数据与视图之间的桥梁。你需要自己维护实际的数据存储。

  • 优点:性能最优(无额外对象开销),架构最清晰,数据与界面完全解耦,扩展性最强。

  • 缺点 :需要手动重写**rowCount()、columnCount()、data()**等几个核心函数,代码量稍多。

方案三:QSqlTableModel(需与数据库交互)

如果你的数据需要持久化存储在数据库中(如SQLite、MySQL ),可以直接使用QSqlTableModel。

  • 特点:直接与数据库表关联,模型的操作会自动同步到数据库。

  • 优点:无需手动编写SQL语句进行增删改查,非常方便。

  • 缺点 :必须依赖数据库,不适合纯内存数据的场景。


总结与建议

模型 推荐场景
QStandardItemModel 数据量不大(< 1万行),快速原型开发,通用场景
QAbstractTableModel 数据量大,数据结构复杂,需要与业务深度绑定
QSqlTableModel 数据需要持久化存储在数据库中

我现在会先用deepseek问一下大体的实现思路,方案的优劣性。

然后让TRAE Work CN来帮我实现一版demo,直接运行看效果。

效果好的话直接引入关键类和程序到项目中。

cpp 复制代码
#ifndef MESSAGETABLEMODEL_H
#define MESSAGETABLEMODEL_H

#include <QAbstractTableModel>
#include <QVector>

struct MessageItem {
    QString commandCode;
    QString senderAddress;
    QString receiverAddress;
    QString content;
};

class MessageTableModel : public QAbstractTableModel
{
    Q_OBJECT

public:
    enum Column {
        CommandCode = 0,
        SenderAddress,
        ReceiverAddress,
        Content,
        ColumnCount
    };

    explicit MessageTableModel(QObject *parent = nullptr);

    int rowCount(const QModelIndex &parent = QModelIndex()) const override;
    int columnCount(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 = Qt::DisplayRole) const override;

    Qt::ItemFlags flags(const QModelIndex &index) const override;
    bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;

    bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
    bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;

    void addMessage(const MessageItem &item);
    void clear();

private:
    QVector<MessageItem> m_messages;
};

#endif // MESSAGETABLEMODEL_H
cpp 复制代码
#include "messagetablemodel.h"

MessageTableModel::MessageTableModel(QObject *parent)
    : QAbstractTableModel(parent)
{
}

int MessageTableModel::rowCount(const QModelIndex &parent) const
{
    return parent.isValid() ? 0 : m_messages.size();
}

int MessageTableModel::columnCount(const QModelIndex &parent) const
{
    return parent.isValid() ? 0 : ColumnCount;
}

QVariant MessageTableModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid() || index.row() < 0 || index.row() >= m_messages.size())
        return {};

    if (role == Qt::DisplayRole || role == Qt::EditRole) {
        const MessageItem &item = m_messages.at(index.row());
        switch (index.column()) {
        case CommandCode:      return item.commandCode;
        case SenderAddress:    return item.senderAddress;
        case ReceiverAddress:  return item.receiverAddress;
        case Content:          return item.content;
        }
    }

    if (role == Qt::TextAlignmentRole) {
        return Qt::AlignCenter;
    }

    return {};
}

QVariant MessageTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if (role != Qt::DisplayRole)
        return {};

    if (orientation == Qt::Horizontal) {
        switch (section) {
        case CommandCode:      return QStringLiteral("命令码");
        case SenderAddress:    return QStringLiteral("发送方地址");
        case ReceiverAddress:  return QStringLiteral("接收方地址");
        case Content:           return QStringLiteral("报文内容");
        }
    }
    return section + 1;
}

Qt::ItemFlags MessageTableModel::flags(const QModelIndex &index) const
{
    if (!index.isValid())
        return QAbstractTableModel::flags(index);
    return QAbstractTableModel::flags(index) | Qt::ItemIsEditable;
}

bool MessageTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    if (!index.isValid() || role != Qt::EditRole)
        return false;

    MessageItem &item = m_messages[index.row()];
    switch (index.column()) {
    case CommandCode:      item.commandCode     = value.toString(); break;
    case SenderAddress:    item.senderAddress   = value.toString(); break;
    case ReceiverAddress:  item.receiverAddress = value.toString(); break;
    case Content:          item.content         = value.toString(); break;
    default: return false;
    }

    emit dataChanged(index, index, {role});
    return true;
}

bool MessageTableModel::insertRows(int row, int count, const QModelIndex &parent)
{
    if (parent.isValid())
        return false;

    beginInsertRows(parent, row, row + count - 1);
    for (int i = 0; i < count; ++i)
        m_messages.insert(row, MessageItem{});
    endInsertRows();
    return true;
}

bool MessageTableModel::removeRows(int row, int count, const QModelIndex &parent)
{
    if (parent.isValid() || row < 0 || row + count > m_messages.size())
        return false;

    beginRemoveRows(parent, row, row + count - 1);
    for (int i = 0; i < count; ++i)
        m_messages.removeAt(row);
    endRemoveRows();
    return true;
}

void MessageTableModel::addMessage(const MessageItem &item)
{
    int row = m_messages.size();
    beginInsertRows(QModelIndex(), row, row);
    m_messages.append(item);
    endInsertRows();
}

void MessageTableModel::clear()
{
    if (m_messages.isEmpty())
        return;
    beginResetModel();
    m_messages.clear();
    endResetModel();
}
cpp 复制代码
#include <QApplication>
#include <QTableView>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPushButton>
#include <QInputDialog>
#include <QHeaderView>
#include <QWidget>

#include "messagetablemodel.h"

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    auto *model = new MessageTableModel;
    for(int i = 0;i < 200000;i++)
    {
        model->addMessage({"0x01", "0xAA", "0xBB", "Hello World aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"});
        model->addMessage({"0x02", "0xCC", "0xDD", "Request Data bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"});
        model->addMessage({"0x03", "0xEE", "0xFF", "ACK ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"});
    }


    auto *tableView = new QTableView;
    tableView->setModel(model);
    tableView->horizontalHeader()->setStretchLastSection(true);
    tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
    tableView->setEditTriggers(QAbstractItemView::DoubleClicked | QAbstractItemView::EditKeyPressed);
    tableView->resizeColumnsToContents();


    auto *mainLayout = new QVBoxLayout;
    mainLayout->addWidget(tableView);

    QWidget window;
    window.setWindowTitle(QStringLiteral("报文模型视图示例"));
    window.resize(700, 400);
    window.setLayout(mainLayout);
    window.show();


    return app.exec();
}

效果蛮好的

相关推荐
慧都小项4 天前
当边缘AI上产线:QtitanDocking 如何让工业 HMI 实现多视图协同
人工智能·qt·ui·边缘计算·qt6.3
Quz4 天前
QML SwipeDelegate 滑动委托
qt
Quz4 天前
QML 列表中的四种委托:ItemDelegate、CheckDelegate、RadioDelegate、SwitchDelegate
qt
实心儿儿4 天前
Qt — 显示类控件
qt
江湖人称菠萝包5 天前
【Qt】《Qt 5.9 C++开发指南》笔记-Chapter9-Qt Charts
笔记·qt·qt5
扶尔魔ocy5 天前
【QT android】环境安装及第一个QT项目
qt·andriod开发
江湖人称菠萝包5 天前
【Qt】《Qt 5.9 C++开发指南》笔记-Chapter10-Data Visualization
笔记·qt·qt5
Cx330❀5 天前
Qt 常用控件属性与底层机制详解:从窗口半透明、浮点数陷阱到 QSS 与焦点策略
qt·ui·图形渲染
Quz5 天前
QML DelegateChooser:多条件组合与按列选择委托
qt
Quz5 天前
QML DelegateChooser:按角色选择委托与按索引选择委托
qt