PDA

View Full Version : How to sort a QComboBox in Qt4



guenthk
21st September 2006, 09:24
In Qt3 you could access the list box of a combo box and easily sort its items by calling the sort method of the list box.

In Qt4 you can get the abstract item model of a combo box, but the implementation of the sort method of QAbstractItemModel does nothing.

Is there a similarly simple way as in Qt3 to sort the items of a QComboBox in Qt4 after the combo box has been initialized by inserting items in any order?

(I have seen thread http://www.qtcentre.org/forum/showthread.php?t=1249&highlight=sorting+combo, but it doesn't fully answer my question.)

Many thanks in advance,

Klaus.

jpn
21st September 2006, 10:03
combo->addItems(QStringList() << "B" << "C" << "A"); // "B","C","A"
// for sorting you need the following 4 lines
QSortFilterProxyModel* proxy = new QSortFilterProxyModel(combo); // <--
proxy->setSourceModel(combo->model()); // <--
// combo's current model must be reparented,
// otherwise QComboBox::setModel() will delete it
combo->model()->setParent(proxy); // <--
combo->setModel(proxy); // <--
// sort
combo->model()->sort(0); // "A","B","C"

guenthk
21st September 2006, 11:54
Many thanks for your immediate reply.

Unfortunately the proposed solution doesn't fully work since our combo box comprises user data, and the latter seems to disappear when applying your proposal.

Do you know an easy way how to keep it?

Klaus.

jpn
21st September 2006, 14:30
It does? QComboBox uses a QStandardItemModel for storing it's items so I don't see any reason why wouldn't a QSortFilterProxyModel work in between.



// add items with user data
combo->addItem("B", "B's data");
combo->addItem("C", "C's data");
combo->addItem("A", "A's data");

// sort filter proxy model has been created and set like in the earlier post..

for (int i = 0; i < combo->count(); ++i)
qDebug() << combo->itemText(i) << combo->itemData(i).toString();
// outputs before sorting:
// "B" "B's data"
// "C" "C's data"
// "A" "A's data"

// sort
combo->model()->sort(0);

for (int i = 0; i < combo->count(); ++i)
qDebug() << combo->itemText(i) << combo->itemData(i).toString();
// outputs after sorting:
// "A" "A's data"
// "B" "B's data"
// "C" "C's data"

guenthk
22nd September 2006, 09:07
I haven't found out so far, why the user data (which is a pointer to a user-defined class) disappears (i.e., access to itemData yields a "0" pointer).

But I have another problem with your proposal:

The contents of our combo box is changed and re-sorted again and again, depending on user actions. According to your proposal, the combo box model would be replaced then on every sort operation while the original model would be retained, too. So we would get an increasing number of models this way.

As I have understood the model/view philosophy of Qt4 and of QSortFilterProxyModel, the correct procedure as to sorting of list views is to insert an intermediate QSortFilterProxyModel between the base/source model and the view without replacing the source model, and then to sort the QSortFilterProxyModel.

I have tried this out, but I get a crash which seems to be caused by the fact that the QSortFilterProxyModel doesn't have a persistent model index. Again I don't know the reason for that, nor how I could enforce the construction of a persistent model index. But in principle I believe that the construction of an intermediate QSortFilterProxyModel is what TrollTech has in mind as the proper solution of the sorting problem.

I have posted the issue also to the TrollTech bug/task tracker, but I am uncertain if they will give me a satisfactory answer.

Klaus.

jpn
22nd September 2006, 17:11
The contents of our combo box is changed and re-sorted again and again, depending on user actions. According to your proposal, the combo box model would be replaced then on every sort operation while the original model would be retained, too. So we would get an increasing number of models this way.
Well, sorry if I was unclear, but this is not what I meant. I meant that the proxy model would be applied only once.


As I have understood the model/view philosophy of Qt4 and of QSortFilterProxyModel, the correct procedure as to sorting of list views is to insert an intermediate QSortFilterProxyModel between the base/source model and the view without replacing the source model, and then to sort the QSortFilterProxyModel.
Yes, this is correct and also exactly the way I wanted it to work with combo box too..

I noticed something funny. Sorting items between insertions makes items disappear, indeed. The sorting seems to work only once or something. Try uncommenting the first line doing the sort.



#include <QtGui>

class SortableComboBox : public QComboBox
{
public:
SortableComboBox(QWidget* parent = 0)
: QComboBox(parent)
{
QStandardItemModel* model = new QStandardItemModel(0, 1, this);
QSortFilterProxyModel* proxy = new QSortFilterProxyModel(this);
proxy->setSourceModel(model);
setModel(proxy);
}
};

int main(int argc, char* argv[])
{
QApplication a(argc, argv);
SortableComboBox combo;

combo.addItem("B");
combo.addItem("C");
combo.addItem("A");

// combo.model()->sort(0); // <- uncomment this line and items disappear..

combo.addItem("F");
combo.addItem("E");
combo.addItem("D");

combo.model()->sort(0);

combo.show();
return a.exec();
}

guenthk
25th September 2006, 09:40
Many thanks again for your effort.

I think we should try to do without the Qt4 sorting support and write a sorted insert operation for our combo boxes instead. (Trolltech has promised in their bug tracker to provide this feature in Qt 4.3).

I think you should submit your funny sorting example to the Trolltech bug tracker, too. (Or shall I?)

regards,

Klaus.

jpn
25th September 2006, 19:35
The trolls answered that this has already been fixed to Qt 4.2. I tested with 4.2.0-rc1 and no items disappeared indeed. However, the second sort doesn't work. Meaning that the final result is incorrect (A,B,C,F,E,D). Could someone with a recent snapshot of Qt 4.2 compiled test this before I bug them more? ;)