PDA

View Full Version : QListView sorting



fear
20th August 2007, 17:30
I'd like to subclass QListView in the way it will only emit some signal when user makes sort request (click on header's column). I cannot turn off sorting with setSorting(-1) because then I also lose sort indicator. I have native method in my collection that takes care of sorting so QListView should only emit signal, native method will sort items and finally QListView will be cleared and filled with new items in new order. I've tried to reimplement QListView::sort(), QListView::enforceSortOrder() and QListViewItem::sortChildItems(int, bool). Despite the fact I was browsing qlistview.cpp carefully I still don't know what method actually sorts QListViewItems when user changes sort order by clicking header's column.

wysota
20th August 2007, 19:18
How about reimplementing QListView::sort() and do the sorting there (or forward it elsewhere)? What went wrong when you tried to use it?

fear
20th August 2007, 19:23
How about reimplementing QListView::sort() and do the sorting there (or forward it elsewhere)? What went wrong when you tried to use it?

Simply these methods are NOT (except QListViewItem::sortChildItems(int, bool) - but it sorts childs of every QListViewItem not QListViewItems) called when user clicks header's column.



void QListViewItem::sort()
{
if ( !listView() )
return;
lsc = Unsorted;
enforceSortOrder();
listView()->triggerUpdate();
}

void QListViewItem::enforceSortOrder() const
{
QListView *lv = listView();
if ( !lv || lv && (lv->d->clearing || lv->d->sortcolumn == Unsorted) )
return;
if ( parentItem &&
(parentItem->lsc != lsc || parentItem->lso != lso) )
((QListViewItem *)this)->sortChildItems( (int)parentItem->lsc,
(bool)parentItem->lso );
else if ( !parentItem &&
( (int)lsc != lv->d->sortcolumn || (bool)lso != lv->d->ascending ) )
((QListViewItem *)this)->sortChildItems( lv->d->sortcolumn, lv->d->ascending );
}

void QListViewItem::sortChildItems( int column, bool ascending )
{
// we try HARD not to sort. if we're already sorted, don't.
if ( column == (int)lsc && ascending == (bool)lso )
return;

if ( column < 0 )
return;

lsc = column;
lso = ascending;

const int nColumns = ( listView() ? listView()->columns() : 0 );

// only sort if the item have more than one child.
if ( column > nColumns || childItem == 0 || (childItem->siblingItem == 0 && childItem->childItem == 0))
return;

// make an array for qHeapSort()
QListViewPrivate::SortableItem * siblings
= new QListViewPrivate::SortableItem[nChildren];
QListViewItem * s = childItem;
int i = 0;
while ( s && i < nChildren ) {
siblings[i].numCols = nColumns;
siblings[i].col = column;
siblings[i].asc = ascending;
siblings[i].item = s;
s = s->siblingItem;
i++;
}

// and sort it.
qHeapSort( siblings, siblings + nChildren );

// build the linked list of siblings, in the appropriate
// direction, and finally set this->childItem to the new top
// child.
if ( ascending ) {
for( i = 0; i < nChildren - 1; i++ )
siblings[i].item->siblingItem = siblings[i+1].item;
siblings[nChildren-1].item->siblingItem = 0;
childItem = siblings[0].item;
} else {
for( i = nChildren - 1; i > 0; i-- )
siblings[i].item->siblingItem = siblings[i-1].item;
siblings[0].item->siblingItem = 0;
childItem = siblings[nChildren-1].item;
}
for ( i = 0; i < nChildren; i++ ) {
if ( siblings[i].item->isOpen() )
siblings[i].item->sort();
}
delete[] siblings;
}


In fact only last method does real sorting. Items were sorted desipte the fact I've reimplemented it.

wysota
20th August 2007, 19:50
sort() is a method of the LISTVIEW, not of the ITEM.

fear
20th August 2007, 21:14
sort() is a method of the LISTVIEW, not of the ITEM.

I reimplemented it in subclass of QListView.



class KMListView : public KListView
{
Q_OBJECT

public:
KMListView(QWidget * parent = 0, const char * name = 0);
~KMListView();

void setSelected(QListViewItem * item, bool selected);
void clearSelection();
QListViewItem * selectedItem() const;
void sort();

public slots:
void selectAll();

signals:
void mouseButtonReleased(const QPoint &);
void keyPressed(QKeyEvent * e);
void rightMouseButtonClicked(const QPoint &);
void sortRequested(const int, const SortOrder);

protected:
bool acceptDrag(QDropEvent * event) const;
void contentsMousePressEvent(QMouseEvent * e);
void contentsMouseReleaseEvent(QMouseEvent * e);
void contentsMouseMoveEvent(QMouseEvent * e);
void keyPressEvent(QKeyEvent * e);
void enforceSortOrder() const;

private:
bool dndPerm;
};




void KMListView::sort()
{
qDebug("sort");

emit sortRequested(sortColumn(), sortOrder());
}


sort() is executed only when I call it:



KMListView * lv = new KMListView(this);
lv->sort();

wysota
20th August 2007, 22:36
Did you enable sorting on the list view? As far as I remember you have to access the header and make it click enabled (I'm not sure though).

fear
20th August 2007, 22:42
My settings:



lvi->setSelectionMode(QListView::Extended);
lvi->setShowSortIndicator(true);
lvi->setShadeSortColumn(true);
lvi->setResizeMode(QListView::NoColumn);
lvi->setDropVisualizer(false);
lvi->setAllColumnsShowFocus(true);


Problem is I don't know what actually sorts items! As I mentioned sort() is not called (but QListViewItems are sorted!).

KMAPSRULE
20th August 2007, 23:33
I think What you want to do is overload the < operator in the List Item.

This example is from a QT 4.1 QTreeWidgetItem I had to implement sorting on when the TreeView Headers were Clicked, but the Idea should be the Same for the QT3 QListViewItem:


bool operator<(const QTreeWidgetItem & other) const
{
int column = treeWidget()->sortColumn();
if ( column == 0 || column == 2 )
{
int thisInt = text(column).toInt();
int otherInt = other.text(column).toInt();
return thisInt < otherInt;
}
// sort date and time columns as one
else if ( column == 3 || column == 4 )
{
QDate thisDate = QDate::fromString(text(3), "yyyy/MM/dd");
QDate otherDate = QDate::fromString(other.text(3), "yyyy/MM/dd");
if ( thisDate == otherDate )
{
QTime thisTime = QTime::fromString(text(4));
QTime otherTime = QTime::fromString(other.text(4));
return thisTime < otherTime;
}
else
{
return thisDate < otherDate;
}
}
else if ( column == 5 )
{
QTime thisTime = QTime::fromString(text(column), "h:mm:ss");
QTime otherTime = QTime::fromString(other.text(column), "h:mm:ss");
return thisTime < otherTime;
}


return QTreeWidgetItem::operator <(other); //otherwise use base class

}//end operator < overload

fear
20th August 2007, 23:46
I can't do it because operator< is part of SortableItem class.

wysota
21st August 2007, 00:08
At worst you can reimplement compare() from QListViewItem.

KMAPSRULE
21st August 2007, 01:24
Right,
Sorry About that, havent used QT3 in a while or QT4, I'm getting rusty
in QT3 QListView you need to reimplement compare()
see this thread (http://www.qtcentre.org/forum/f-qt-programming-2/t-items-in-qlistview-should-sort-on-header-click-4228.html)

fear
21st August 2007, 08:16
I've reimplemented compare in this way:



int KMListViewItem::compare(QListViewItem * i, int col, bool ascending) const
{
qDebug("compare()");

Q_UNUSED(i);
Q_UNUSED(col);
Q_UNUSED(ascending);

return 0;
}


It should work but it doesn't (items are moved). I think the only solution is to use setSorting(-1) and then use QHeader's setSortIndicator.

wysota
21st August 2007, 09:02
Try implementing a real compare function. This one is not valid.

fear
21st August 2007, 10:30
I think the only solution is to use setSorting(-1) and then use QHeader's setSortIndicator.

I made it this way and now it works! Reimplementing compare() isn't good idea because list view will still iterate over items.

wysota
21st August 2007, 14:08
http://doc.trolltech.com/qq/qq01-sorting-qlistviews.html