QAbstractTableModel基本使用:数据展示与编辑

前言

QAbstractTableModel 继承自 QAbstractItemModel,主要用于为 QTableView 提供相关接口,我们可以子类化该抽象类并实现相关接口。本文主要讲 QAbstractTableModel 数据展示和编辑相关的接口如何使用。

表格数据的展示

继承 QAbstractTableModel 后,至少要实现三个纯虚函数接口才能进行实例化:

virtual int rowCount(const QModelIndex &parent = QModelIndex()) const = 0;
virtual int columnCount(const QModelIndex &parent = QModelIndex()) const = 0;
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const = 0;

前两个接口比较简单,直接返回展示表格的行列,参数忽略(这个 parent 参数主要是给树形列表用的)。我们主要关注 data 这个接口,当 view 或者 delegate 需要从 model 获取单元格相关数据(如显示内容、颜色等)时会调用该接口,只有两个参数,模型索引和 role ,数据通过返回值传出来。

QModelIndex 模型索引,提供了对信息的临时引用,可用于通过模型检索或修改数据,一般情况下我们只需要取他附带的行列值即可。

获取数据的类型是通过 role 角色参数来区分的,这是一个 Qt::ItemDataRole 枚举,一般用已有的 role 枚举就足够了。 

    enum ItemDataRole {
        DisplayRole = 0,
        DecorationRole = 1,
        EditRole = 2,
        ToolTipRole = 3,
        StatusTipRole = 4,
        WhatsThisRole = 5,
        // Metadata
        FontRole = 6,
        TextAlignmentRole = 7,
        BackgroundRole = 8,
        ForegroundRole = 9,
#if QT_DEPRECATED_SINCE(5, 13) // ### Qt 6: remove me
        BackgroundColorRole Q_DECL_ENUMERATOR_DEPRECATED = BackgroundRole,
        TextColorRole Q_DECL_ENUMERATOR_DEPRECATED = ForegroundRole,
#endif
        CheckStateRole = 10,
        // Accessibility
        AccessibleTextRole = 11,
        AccessibleDescriptionRole = 12,
        // More general purpose
        SizeHintRole = 13,
        InitialSortOrderRole = 14,
        // Internal UiLib roles. Start worrying when public roles go that high.
        DisplayPropertyRole = 27,
        DecorationPropertyRole = 28,
        ToolTipPropertyRole = 29,
        StatusTipPropertyRole = 30,
        WhatsThisPropertyRole = 31,
        // Reserved
        UserRole = 0x0100
    };

最后是返回数据,这是一个 QVariant 类型的值,可以理解为任意类型。对于 Qt 常用的数据类型可以直接用 toXXX 接口转换,自定义类型可以用 convert 和 value 接口装箱拆箱。

了解了接口之后来一个简单的例子,看看具体是怎么用的:

#include <QAbstractTableModel>

class MyTableModel : public QAbstractTableModel
{
    Q_OBJECT
    struct ModelItem
    {
        QString name;
        int sex;
        int age;
        int score;
    };

public:
    explicit MyTableModel(QObject *parent = nullptr)
        : QAbstractTableModel(parent)
    {
        modelData=QList<ModelItem>{
        {"aa",1,20,90},
        {"bb",1,23,91},
        {"cc",0,21,95},
    };
    }

    //获取行数
    int rowCount(const QModelIndex &parent = QModelIndex()) const override
    {
        return modelData.count();
    }
    //获取列数
    int columnCount(const QModelIndex &parent = QModelIndex()) const override
    {
        return 4;
    }
    //获取单元格数据
    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
    {
        if(!index.isValid())
            return QVariant();
        if(role == Qt::DisplayRole || role == Qt::EditRole)
        {
            const int row=index.row();
            switch(index.column())
            {
            case 0: return modelData.at(row).name;
            case 1: return (modelData.at(row).sex==0)?"woman":"man";
            case 2:return modelData.at(row).age;
            case 3:return modelData.at(row).score;
            }
        }
        return QVariant();
    }

private:
    //数据
    QList<ModelItem> modelData;
};

然后使用 view 的 setModel 接口将 new 出来的 model 实例设置给 view:

    MyTableModel *model=new MyTableModel(ui->tableView);
    ui->tableView->setModel(model);

显示如下:

单元格启用编辑

如果使用通用的编辑功能(通过 view 的 editTriggers 设置触发编辑状态的方式,一般为双击),model 需要实现两个接口:

//设置单元格数据
bool setData(const QModelIndex &index, const QVariant &value,int role = Qt::EditRole) override;
//单元格的可操作性标志位,如可编辑,可选中等
Qt::ItemFlags flags(const QModelIndex& index) const override;

flags 接口告诉我们某个单元格支持哪些操作,返回一个 Qt::ItemFlags 枚举值:

    enum ItemFlag {
        NoItemFlags = 0,
        ItemIsSelectable = 1,
        ItemIsEditable = 2,
        ItemIsDragEnabled = 4,
        ItemIsDropEnabled = 8,
        ItemIsUserCheckable = 16,
        ItemIsEnabled = 32,
        ItemIsAutoTristate = 64,
#if QT_DEPRECATED_SINCE(5, 6)
        ItemIsTristate = ItemIsAutoTristate,
#endif
        ItemNeverHasChildren = 128,
        ItemIsUserTristate = 256
    };

如果仅仅是需要编辑,只需要返回 :

    return Qt::ItemIsEnabled|Qt::ItemIsEditable;

当我们双击单元格,允许编辑的单元格会创建一个编辑组件(这是由 delegate 完成的),我们修改数据后回车或切换焦点完成编辑,编辑好的数据会通过 setData 接口设置给 model。

setData 接口参数含义和 data 差不多,只是有一个设置成功或失败的返回值,接下来看下基本的写法:

bool MyTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    if (index.isValid() && role == Qt::EditRole) {
        const int row = index.row();
        switch(index.column())
        {
        case 0: modelData[row].name = value.toString(); break;
        case 1: modelData[row].sex = (value.toString() == "man")?1:0; break;
        case 2: modelData[row].age = value.toInt(); break;
        case 3: modelData[row].score = value.toInt(); break;
        }
        //发送信号触发刷新
        emit dataChanged(index, index, QVector<int>() << role);
        return true;
    }
    return false;
}

Qt::ItemFlags MyTableModel::flags(const QModelIndex &index) const
{
    if (!index.isValid())
        return Qt::NoItemFlags;
    return Qt::ItemIsEnabled|Qt::ItemIsSelectable|Qt::ItemIsEditable;
}

注意,修改数据后发送 dataChanged 信号 view 才会刷新对应索引的单元格。 

结束语

通过上面的操作,我们已经可以显示和编辑数据了。但是聪明的你已经看出来了这是固定的数据列表,而不是动态加载的,这部分内容我放到下一篇写,主要是讲如何导入导出数据、增删行列等。

参考

官方文档:https://doc.qt.io/qt-5/qabstracttablemodel.html

官方文档:https://doc.qt.io/qt-5/model-view-programming.html

使用 AbstractTableModel 构建Table 在表格中添加JButton按钮,之前在网上找了2天没有找到好用的程序,最终终于找到一个好用的例子。 不要使,我退你们分。。 sing the Swing JTable class can quickly become a sticky business when you want to customize it to your specific needs. First you must become familiar with how the JTable class is organized. Individual cells are rendered by TableCellRenderer implementations. The table contents are represented by an implementation of the TableModel interface. By default, JTable uses DefaultTableCellRenderer to draw its cells. DefaultTableCellRenderer recognizes a few primitive types, rendering them as strings, and can even display Boolean types as checkboxes. But it defaults to displaying the value returned by toString() for types it does not specifically handle. You have to provide your own TableCellRenderer implementation if you want to display buttons in a JTable. The TableCellRenderer interface contains only one method, getTableCellRendererComponent(...), which returns a java.awt.Component that knows how to draw the contents of a specific cell. Usually, getTableCellRendererComponent() will return the same component for every cell of a column, to avoid the unnecessary use of extra memory. But when the contents of a cell is itself a component, it is all right to return that component as the renderer. Therefore, the first step towards having JButtons display correctly in a JTable is to create a TableCellRenderer implementation that returns the JButton contained in the cell being rendered. In the accompanying code listing, JTableButtonRenderer demonstrates how to do this. Even after creating a custom TableCellRenderer, you're still not done. The TableModel associated with a given JTable does not only keep track of the contents of each cell, but it also keeps track of the class of data stored in each column. DefaultTableModel is designed to work with DefaultTableCellRenderer and will return java.lang.String.class for columns containing data types that it does not specifically handle. The exact method that does this is getColumnClass(int column). Your second step is to create a TableModel implementation that returns JButton.class for cells that contain JButtons. JTableButtonModel shows one way to do this. It just returns the result of getClass() for each piece of cell data. At this point, you're almost done, but not quite. What's the use of putting a JButton in a JTable if you can't press the darn thing? By default, JTable will not forward mouse events to components contained in its cells. If you want to be able to press the buttons you add to JTable, you have to create your own MouseListener that forwards events to the JButton cells. JTableButtonMouseListener demonstrates how you could do this.
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

龚建波

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值