Сортировка QListViews
Материал из Wiki.crossplatform.ru
Qt Quarterly | Выпуск 1 | Документация |
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@bloggs.com>", "Joe <joe@bloggs.com>" и "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(). Можно отсортировать все!