Wiki

Сортировка QListViews

Andy Shaw (перевод Racheengel)

Компонент QListView обеспечивает отображение данных в виде списков и "деревьев". Класс поддерживает независимую от локали юникодовую сортировку по любому столбцу (либо ее отсутствие). Часто бывает необходимым реализовать более сложный метод сортировки, чем стандартный лексикографический. В данной статье показано, как это можно сделать.

По умолчанию, QListView сортирует по первой колонке в возрастающем порядке. Чтобы отключить сортировку, надо вызвать setSorting(-1). Чтобы указать, по какой колонке производить сортировку, надо вызвать setSorting() с номером колонки и, если необходимо, с флагом порядка (возрастающий/убывающий) Если два элемента имеют одинаковые ключи в указанной колонке, Qt (начиная с 3.0.2) использует при необходимости информацию из других колонок. Нестандартный способ сортировки может быть задан при реимплементации одной из следующих функций QListViewItem: key(), compare() или sortChildItems().

Реимплементация sortChildItems() "перекрывает" key() и compare(), и позволяет вам изменить базовый алгоритм, используемый в qHeapSort() , на ваш собственный. В данной статье мы опишем самые общие варианты реимплементации key() или compare().

Пример почтового клиента

Мы будем использовать почтовый клиент как пример применения нестандартной сортировки в QListView. Мы создадим QListView с колонками Subject, Sender и Date.

QListView* mail = new QListView( this );
mail->addColumn( "Subject" );
mail->addColumn( "Sender" );
mail->addColumn( "Date" );

Заполним клиент некоторыми данными.

new QListViewItem( mail, "Accounts",
    "Joe Bloggs <joe@bloggs.com>", "12/25/2001" );
new QListViewItem( mail, "Re: Accounts",
    "andy@nospam.com", "12/31/2001" );
new QListViewItem( mail, "Expenses",
    "joe@bloggs.com", "01/08/2002" );
new QListViewItem( mail, "Re: Accounts",
    "Joe <joe@bloggs.com>", "01/17/2002" );
new QListViewItem( mail, "Re: Expenses",
    "Andy <andy@nospam.com>", "02/01/2002" );

Реимплементация QListViewItem::key()

Эта функция чаще всего используется для сравнения текстовых данных. Реализация по умолчанию просто возвращает текст в соответствующей колонке.

Если мы отсортировали список по колонке Sender, она расположит "Joe Bloggs ", "Joe " и "joe@bloggs.com" в различных местах, даже если они представляют тот же почтовый адрес. Мы можем решить данную проблему реимплементацией функции key(), чтобы она возвращала только адрес, без имени.

class MyListViewItem : public QListViewItem
{
public:
MyListViewItem( QListView* parent, QString subject,
		QString sender, QString date );
QString key( int column, bool ascending ) const;
};
 
QString MyListViewItem::key( int column,
			 bool ascending ) const
{
if ( column == 1 ) {
    QString senderText = text( 1 );
    int firstbracket = senderText.find( "<" );
    if ( firstbracket == -1 )
	return senderText;
    else
	return senderText.mid( firstbracket + 1,
			       senderText.length() -
			       firstbracket - 2 );
} else {
    return QListViewItem::key( column, ascending );
}
}

Если в колонке Sender содержится "<", мы возьмем текст между угловыми скобками за почтовый адрес; иначе, мы возьмем весь текст. Если QListView не отсортирован по колонке Sender, мы используем функцию key().

Подобное улучшение может быть сделано для расположения "Accounts" и "Re: Accounts" рядом, еогда список отсортирован по колонке Subject.

Заменой key() , как было сделано выше, можно преобразовать любые данные в строку, подходящую для сравнения средствами QListView.

Реимплементация QListViewItem::compare()

После реимплементации функции key(), результирующие строки сравниваются как простой текст, независимо от локали. Для данных, которые не должны быть независимым от локали, а также для повышения производительности, можно переопределить функцию compare().

В нашем примере, при сортировке по колонке Date должно применяться не текстовое сравнение, поэтому это идеальный кандидат для реимплементации compare().

int MyListViewItem::compare( QListViewItem* item,
			 int column,
			 bool ascending ) const
{
if ( column == 2 ) {
    QDate d = QDate::fromString( text( 2 ), Qt::LocalDate );
    QDate e = QDate::fromString( item->text( 2 ), Qt::LocalDate );
    return e.daysTo( d );
} else {
    return QListViewItem::compare( item, column, ascending );
}
}

Данный код преобразует текстовые даты в объекты QDate и возвращает разницу в днях (которая может быть отрицательной). Если даты были сохранены в формате ISO (YYYY-MM-DD), мы можем сравнить их текстово (в compare(), а не в key(), т.к. нам не нужна независимость от локали), что может быть быстрее.

Итоги

Встроенная в QListView сортировка подходит для большинства ситуаций. Наследование с реимплементацией key() или compare() - простой путь для достижения полного контроля над сортировкой. Техника, приведенная здесь, может быть применена где угодно в Qt, например, QIconViewItem::compare(), QListBox::text(), QTableItem::key(). Можно отсортировать все!

Copyright © 2002 Trolltech. Trademarks