QComboBox与自定义模型

时间:2016-01-25 16:40:44

标签: c++ qt qt5

我有自定义存储空间,我希望实现ListModelQComboBox显示它。为简单起见,我们假设我们有一个int列表作为模型的数据,所以这是我对模型和测试程序的实现:

#include <QApplication> 
#include <QDebug>
#include <QAbstractListModel>
#include <QComboBox>
#include <QHBoxLayout>
#include <QPushButton>

QList<int> list;

class MyModel : public QAbstractListModel
{
public:
    MyModel( QWidget* parent ):
        QAbstractListModel( parent )
    {}
    ~MyModel()
    {
        qDebug() << __FUNCTION__;
    }

    QVariant data(const QModelIndex &index, int role) const
    {
        if ( !index.isValid() )
            return QVariant();

        if ( ( role == Qt::DisplayRole ) && ( index.row() < list.size() ) )
            return QString::number( list.at( index.row() ) );

        return QVariant();
    }

    int rowCount(const QModelIndex &parent) const
    {
        Q_UNUSED( parent )
        return list.size();
    }
};

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

    QWidget w;
    QHBoxLayout* l = new QHBoxLayout();

    QComboBox* c = new QComboBox( &w );
    c->setModel( new MyModel( c ) );
    l->addWidget( c );

    QPushButton* b = new QPushButton("+");
    QObject::connect( b, &QPushButton::clicked, [](){
        list.push_back( qrand() );
        qDebug() << list;
    } );
    l->addWidget( b );

    b = new QPushButton("-");
    QObject::connect( b, &QPushButton::clicked, [](){
        if ( !list.isEmpty() )
            list.pop_back();
        qDebug() << list;
    } );
    l->addWidget( b );
    w.setLayout( l );
    w.show();

    return app.exec ();
}

如果我只点击按钮添加一次,然后检查列表,它看起来没问题,但是当我再次点击它时,QComboBox只显示第一个值和一个空行;继续添加新元素对QComboBox没有影响。 如果我多次点击按钮添加,我就会在ComboBox中看到正确的列表。

我无法理解发生了什么以及我做错了什么。

我在Qt5.5.1上使用VS2013 x32 Windows 7

3 个答案:

答案 0 :(得分:4)

当数据实际发生变化时,您的模型需要发出适当的信号。这通常通过调用受保护的函数beginInsertRows,endInsertRows等来完成......由于它们受到保护,因此只能由MyModel内的成员函数调用它们。此外,为了更好地练习,实际数据(您的list)只能由MyModel修改。

首先,让list成为MyModel的私人成员。不应该从外面访问它。

private:
    QList<int> list;

在MyModel中添加两个公共函数来管理list

public:
    void push(int value)
    {
        beginInsertRows(list.size(), list.size());
        list.push_back(value);
        endInsertRows();
    }

    void pop()
    {
        if(list.size()>0)
        {
            beginRemoveRows(list.size()-1, list.size()-1);
            list.pop_back();
            endRemoveRows();
        }
    }

在main中,在模型上保留一个指针

MyModel * m = new MyModel(c);

然后在main()中使用这些功能,而不是直接附加到list

QObject::connect( b, &QPushButton::clicked, [m](){
        m->push(qrand());
    } );

QObject::connect( b, &QPushButton::clicked, [m](){
        m->pop();
    } );

Et voila

替代方案,更多Qt方式

pushpop声明为slots

public slots:
    void push()
    {
        beginInsertRows(list.size(), list.size());
        list.push_back(qrand());
        endInsertRows();
    }

    void pop()
    {
        if(list.size()>0)
        {
            beginRemoveRows(list.size()-1, list.size()-1);
            list.pop_back();
            endRemoveRows();
        }
    }

将它们直接连接到main中的按钮:

QObject::connect( b, &QPushButton::clicked, m, &MyModel::push);

//...

QObject::connect( b, &QPushButton::clicked, m, &MyModel::pop);

答案 1 :(得分:1)

此代码的问题是模型不知道列表中的数据是否已更改。并且QComboBox不知道模型内部的数据也发生了变化。因此,每次更改列表中的数据时,模型都应发出信号layoutAboutToBeChanged(),然后发出layoutChanged()以通知QComboBox有关更改。

答案 2 :(得分:0)

您必须实现其他功能,至少是AbstractItemModel :: insertRows。您必须通知模型有关更改。有关详细信息,请查看docs