C++ 如何使QAbstractTableModel与数据存储保持同步?

C++ 如何使QAbstractTableModel与数据存储保持同步?,c++,qt,qtableview,qabstractitemmodel,C++,Qt,Qtableview,Qabstractitemmodel,在我的应用程序中,我有一个用于保存项目列表的类: class Database : public QObject { Q_OBJECT public: Database(QObject *parent, const QString &name); const Entry& item(int idx) const { Q_ASSERT(idx < itemCount()); return _items.at(idx); } const QS

在我的应用程序中,我有一个用于保存项目列表的类:

class Database : public QObject
{
    Q_OBJECT

public:
    Database(QObject *parent, const QString &name);

    const Entry& item(int idx) const { Q_ASSERT(idx < itemCount()); return _items.at(idx); }
    const QString& name() const { return _name; }
    int itemCount() const { return _items.size(); }

    bool addItem(const Entry &item);
    bool addItems(const Database *source, const QList<int> &idxs);
    bool updateItem(int idx, const Entry &updated);
    void removeItem(int idx);
    void removeItems(const QList<int> &idxs);

private:
    QList<Entry> _items;

signals:
    void itemsRemoved(int start, int count);
    void itemsAdded(int count);
    void itemChanged(int index);
    void countUpdate();
};
我在使模型反映对其当前数据库的更改方面遇到问题。以前,每当数据库中发生变化时(由数据库发送到数据库模型的信号触发),我都会发出一个模型重置来让它工作,但我认为这太过分了。现在我不知道如何正确连接数据库和模型

将数据库信号连接到模型并使模型发出dataChanged()不起作用,因为数据库中的项数(以及模型的行数)正在更改。QAbstractTableModel中有名为rowsInserted()和rowsRemoved()的信号,但文档称它们不能用于自定义类。要重新实现的虚拟函数有removeRows()和insertRows(),但文档中说我必须在它们内部调用begin(Remove | Insert)Rows()和end(Remove | Insert)Rows(),这导致了两个问题:

  • begin…Rows()需要一个QModelIndex“parent”参数,我不知道该使用什么
    编辑:实际上这并不重要,现在我将通过QModelIndex()来实现这一点。QAbstractTreeModel使用它来标识树中的父节点,表模型显然不需要它
  • 文档说,在更改底层数据存储之前,需要调用这些函数

  • 如何使模型与数据库保持同步?谢谢

    我想我明白你的问题了。 一方面,你做了正确的事情,试图将数据与模型分开,另一方面,你的数据并不知道模型本身

    在更改数据之前调用begin…Rows(),然后调用end…Rows()是有原因的。即
    QPersistentModelIndex
    。通常情况下,您不应该hort
    QModelIndex
    对象,但持久索引需要保存和保留。模型必须保证其有效性。看看那些begin…Rows()方法的代码,主要是关于持久索引的

    您有多种选择。
    a) 如果您肯定不会使用持久性索引,那么您可以在模型中实现一个私有插槽,用于侦听来自数据源的某种更新信号。这个插槽只需调用begin…Rows()和end…Rows(),中间没有任何内容。它不是“干净的”,但它会工作的

    b) 您可以在数据源中实现更多信号,一个信号表示数据更改的开始(例如删除或添加行),另一个信号表示所述操作的结束。当然,这将大大增加代码的大小

    c) 你可以让你的
    数据库
    成为模型中的类朋友,并调用begin。。。结束。。。来自数据源中的方法,但是
    数据库
    必须知道该模型

    d) 你可以重新思考这个概念。据我所知,您正在使用
    数据库
    类作为模型的数据存储和代码其他部分的接口,对吗? 使用自定义项和对模型本身进行操作的方法,从而避免麻烦,不是更容易吗?我已经做了我应该做的,所以如果需要的话我可以给你代码

    希望这能有所帮助。

    致以最诚挚的问候

    感谢您不辞劳苦,把它说得那么清楚。我尝试了a)并且成功了,但我不喜欢“不干净”。)最后我做了一些类似c)的事情。现在,数据存储保留了一个指向模型的指针,并调用了它的两个公共函数,这两个函数在对数据进行适当操作之前开始…Rows(),之后结束…Rows()。最后,我想我会做d),也许在下一个版本中进行其他重构无论哪种方式,我都对模型如何知道数据存储中发生了更改以及视图如何更新(只是没有行添加/删除)感到困惑。没有信号从数据存储连接到任何东西。我甚至让它成为非Q_对象,但不知怎么的,它仍然有效。也许你知道幕后发生了什么?谢谢实际上是的。视图不断轮询模型,但这主要适用于可访问角色、装饰角色和工具提示角色等角色。当您从数据中删除一行时,视图将轮询该数据(查找某些内容),并注意到数据已移动。因此,一行消失了,但在底部有一个额外的空行。我很确定,如果在此之后调整视图大小,多余的行将消失,因为这将调用模型的
    rowCount()
    方法。您可以从接口派生模型并向该接口传递指针,而不是向数据源传递模型指针。。这样,数据源只能通过接口访问有限数量的模型类功能。
    class DatabaseModel : public QAbstractTableModel
    {
        Q_OBJECT
    
    public:
        DatabaseModel(QObject *parent = 0);
    
        int rowCount(const QModelIndex &parent = QModelIndex()) const { return _data->itemCount(); }
        int columnCount(const QModelIndex &parent = QModelIndex()) const { return 5; };
    
        QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const = 0;
    
        void setDatabase(const Database *data);
        {
            beginResetModel();
            _data = data;
            endResetModel();
        }
    
    protected:
        const Database *_data;
    };