带有自定义模型的 QComboBox
QComboBox with custom model
我有一个自定义存储,我想实现 ListModel
以使用 QComboBox
显示它。为简单起见,假设我们有一个 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
.
中看到正确的列表
我不明白这是怎么回事,我做错了什么。
我在 Windows 7
上使用 Qt5.5.1
和 VS2013 x32
。
你必须实现额外的功能,至少AbstractItemModel::insertRows。您必须将更改通知模型。有关更多信息,请查看 docs.
此代码的问题在于模型不知道列表中的数据已更改。 QComboBox
也不知道模型中的数据也发生了变化。因此,每次您更改列表中的数据时,您的模型都应该发出信号 layoutAboutToBeChanged()
然后 layoutChanged()
以通知 QComboBox
有关更改。
您的模型需要在数据实际发生变化时发出正确的信号。这通常是通过调用受保护的函数 beginInsertRows、endInsertRows 等来完成的...因为这些是受保护的,所以它们只能由 MyModel
内的成员函数调用。此外,为了良好实践,实际数据(您的 list
)应仅由 MyModel
.
修改
首先,使 list
成为 MyModel
的私有成员。不应从外部访问它。
private:
QList<int> list;
在 MyModel 中添加两个 public 函数用于管理 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();
} );
瞧瞧
另一种,更Qt的方式
将 push
和 pop
声明为 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);
我有一个自定义存储,我想实现 ListModel
以使用 QComboBox
显示它。为简单起见,假设我们有一个 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
.
我不明白这是怎么回事,我做错了什么。
我在 Windows 7
上使用 Qt5.5.1
和 VS2013 x32
。
你必须实现额外的功能,至少AbstractItemModel::insertRows。您必须将更改通知模型。有关更多信息,请查看 docs.
此代码的问题在于模型不知道列表中的数据已更改。 QComboBox
也不知道模型中的数据也发生了变化。因此,每次您更改列表中的数据时,您的模型都应该发出信号 layoutAboutToBeChanged()
然后 layoutChanged()
以通知 QComboBox
有关更改。
您的模型需要在数据实际发生变化时发出正确的信号。这通常是通过调用受保护的函数 beginInsertRows、endInsertRows 等来完成的...因为这些是受保护的,所以它们只能由 MyModel
内的成员函数调用。此外,为了良好实践,实际数据(您的 list
)应仅由 MyModel
.
首先,使 list
成为 MyModel
的私有成员。不应从外部访问它。
private:
QList<int> list;
在 MyModel 中添加两个 public 函数用于管理 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();
} );
瞧瞧
另一种,更Qt的方式
将 push
和 pop
声明为 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);