Класс унаследованный от QHeaderView. Знаю что велосипед, но уже никуда не денешься.
В отсортированном столбце заголовка справа от названия столбца появляется число порядка сортировки и стрелочка (ASC, DESC). Логика работы стрелочек лежит на представлении. Сам HeaderView с моделью по поводу сортировок не общается. Сигналов об изменении порядков, стрелочек не подает. Протестировано под мандривой 2010 с установленной qt 4.5.3.
В paintSection в случае сортировки, я отжевываю справа местечко длиной 35 пикселов и там через структуру стиля вывожу число и стрелочку, и вызываю родительскую отрисовку. Не самый лучший вариант, но быстрый.
Думалось конечно по-взрослому отнаследовать приватный QHeaderView переписать paintSection полностью, но как-то это небинарно совместимо.
// Удалить индикатор сортировки
void removeSortIndicator(int logicalIndex);
// Удалить все индикаторы сортировки
void clearSortIndicators();
// Установлен ли индикатор
bool isSortIndicatorShown(int logicalIndex) const;
// Установить индикатор, если это уже второй индикатор справа отобразить число порядка
// Если clearPrevious все стрелочки в столбцах удаляются
// Если clearPrevious == false добавляется новая стрелочка с номером порядка ее добавления.
void setSortIndicator(int logicalIndex, Qt::SortOrder order, bool clearPrevious = true);
// Какая стрелочка отображена для данной колонки. Если никакой, то результат Qt::Asceding
Qt::SortOrder sortIndicatorOrder(int logicalIndex) const;
// Список столбцов в которых производилась сортировка в том порядке в котором они добавлялись
QList<int> sortIndicatorSections() const;
// Отрисовка
void paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const;
headerview_p.h
#ifndef HEADERVIEW_P_H
#define HEADERVIEW_P_H
#include "kernel/headerview.h"
class QAction;
class HeaderViewPrivate {
Q_DECLARE_PUBLIC(HeaderView);
public:
HeaderViewPrivate(){}
QHash<int, Qt::SortOrder > sortingColumns; //< section, SortOrder>
QList<int> orderList;
HeaderView * q_ptr;
QAction *showHideColumnsActionMenu;
QMenu *showHideColumnsMenu;
QList<QAction *> showHideColumnsActionList;
};
#endif // HEADERVIEW_P_H
headerview.h
#ifndef HEADERVIEW_H
#define HEADERVIEW_H
#include <QtGui/QHeaderView>
class HeaderViewPrivate;
class HeaderView : public QHeaderView
{
Q_OBJECT
Q_DECLARE_PRIVATE(HeaderView);
public:
HeaderView(Qt::Orientation orientation, QWidget* parent = 0);
// MultiSorting
// Удалить индикатор сортировки
void removeSortIndicator(int logicalIndex);
// Удалить все индикаторы сортировки
void clearSortIndicators();
// Установлен ли индикатор
bool isSortIndicatorShown(int logicalIndex) const;
// Установить индикатор, если это уже второй индикатор справа отобразить число порядка
// Если clearPrevious все стрелочки в столбцах удаляются
void setSortIndicator(int logicalIndex, Qt::SortOrder order, bool clearPrevious = true);
// Какая стрелочка отображена для данной колонки. Если никакой, то результат Qt::Asceding
Qt::SortOrder sortIndicatorOrder(int logicalIndex) const;
// Список столбцов в которых производилась сортировка в том порядке в котором они добавлялись
QList<int> sortIndicatorSections() const;
// Отрисовка
void paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const;
// Остальное для менюшки Скрыть/показать столбцы
void setModel(QAbstractItemModel* model);
void headerDataChanged(Qt::Orientation orientation, int logicalFirst, int logicalLast);
virtual void setSectionHidden(int logicalIndex, bool hide);
virtual void hideSection(int logicalIndex);
virtual void showSection(int logicalIndex);
protected slots:
void sectionsAboutToBeRemoved(const QModelIndex &parent, int logicalFirst, int logicalLast);
void sectionsInserted(const QModelIndex& parent, int logicalFirst, int logicalLast);
void showHideAction_toggled(bool checked);
protected:
void checkOneColumn();
HeaderView(HeaderViewPrivate &dd, Qt::Orientation orientation, QWidget* parent);
HeaderViewPrivate * const d_ptr;
};
#endif // HEADERVIEW_H
headerview.cpp
#include "kernel/headerview.h"
#include "headerview_p.h"
#include <QtGui/QApplication>
#include <QtGui/QPainter>
#include <QtGui/QMenu>
#include <QtGui/QAction>
HeaderView::HeaderView(Qt::Orientation orientation, QWidget* parent)
:QHeaderView(orientation, parent)
, d_ptr(new HeaderViewPrivate())
{
Q_D(HeaderView);
d->q_ptr = this;
d->showHideColumnsActionMenu = new QAction("Показать/скрыть столбцы", this);
d->showHideColumnsMenu = new QMenu(this);
d->showHideColumnsActionMenu->setMenu(d->showHideColumnsMenu);
addAction(d->showHideColumnsActionMenu);
}
HeaderView::HeaderView(HeaderViewPrivate &dd, Qt::Orientation orientation, QWidget* parent)
:QHeaderView(orientation, parent)
, d_ptr(&dd)
{
Q_D(HeaderView);
d->q_ptr = this;
}
void HeaderView::removeSortIndicator(int logicalIndex)
{
Q_D(HeaderView);
d->sortingColumns.remove(logicalIndex);
d->orderList.removeAll(logicalIndex);
//repaint();
}
void HeaderView::clearSortIndicators()
{
Q_D(HeaderView);
d->sortingColumns.clear();
d->orderList.clear();
//repaint();
}
bool HeaderView::isSortIndicatorShown(int logicalIndex) const
{
Q_D(const HeaderView);
return (d->sortingColumns.contains(logicalIndex));
}
void HeaderView::setSortIndicator(int logicalIndex, Qt::SortOrder order, bool clearPrevious)
{
Q_D(HeaderView);
if (clearPrevious)
clearSortIndicators();
if (logicalIndex > -1 && logicalIndex < count()) {
d->sortingColumns.insert(logicalIndex, order);
if (!d->orderList.contains(logicalIndex))
d->orderList.append(logicalIndex);
} else if (logicalIndex == -1)
clearSortIndicators();
repaint();
}
QList<int> HeaderView::sortIndicatorSections() const
{
Q_D(const HeaderView);
return d->orderList;
}
Qt::SortOrder HeaderView::sortIndicatorOrder(int logicalIndex) const
{
Q_D(const HeaderView);
if (d->sortingColumns.contains(logicalIndex))
return d->sortingColumns.value(logicalIndex);
return Qt::AscendingOrder;
}
void HeaderView::paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const
{
Q_D(const HeaderView);
if (isSortIndicatorShown(logicalIndex)) {
QRect sortInfoRect(rect.topRight() - QPoint(35,0), rect.bottomRight());
QStyleOptionHeader opt;
initStyleOption(&opt);
QStyle::State state = QStyle::State_None;
if (isEnabled())
state |= QStyle::State_Enabled;
if (window()->isActiveWindow())
state |= QStyle::State_Active;
// not good worked
QPoint cursorPos = mapFromGlobal(cursor().pos());
if (isClickable()) {
if (sortInfoRect.contains(cursorPos)) {
state |= QStyle::State_MouseOver;
if (QApplication::mouseButtons() & Qt::LeftButton)
state |= QStyle::State_Sunken;
}
}
opt.rect = sortInfoRect;
opt.section = logicalIndex;
opt.state |= state;
if (d->orderList.count() > 1) {
int i;
for (i = 0; i < d->orderList.count(); ++i)
if (d->orderList.at(i) == logicalIndex)
break;
opt.text = QString::number(i+1) /*+ (d->sortingColumns.value(logicalIndex)==Qt::AscendingOrder
?QString(0x2193) : QString(0x2191))*/;
}
opt.sortIndicator = d->sortingColumns.value(logicalIndex)==Qt::AscendingOrder
?QStyleOptionHeader::SortDown
:QStyleOptionHeader::SortUp;
QVariant foregroundBrush = model()->headerData(logicalIndex, orientation(),
Qt::ForegroundRole);
if (qVariantCanConvert<QBrush>(foregroundBrush))
opt.palette.setBrush(QPalette::ButtonText, qvariant_cast<QBrush>(foregroundBrush));
QPointF oldBO = painter->brushOrigin();
QVariant backgroundBrush = model()->headerData(logicalIndex, orientation(),
Qt::BackgroundRole);
if (qVariantCanConvert<QBrush>(backgroundBrush)) {
opt.palette.setBrush(QPalette::Button, qvariant_cast<QBrush>(backgroundBrush));
opt.palette.setBrush(QPalette::Window, qvariant_cast<QBrush>(backgroundBrush));
painter->setBrushOrigin(opt.rect.topLeft());
}
//the section position
//int visual = visualIndex(logicalIndex);
//Q_ASSERT(visual != -1);
//if (count() == 1)
//opt.position = QStyleOptionHeader::OnlyOneSection;
//else if (visual == 0)
//opt.position = QStyleOptionHeader::Beginning;
//else if (visual == count() - 1)
opt.position = QStyleOptionHeader::End;
//else
//opt.position = QStyleOptionHeader::Middle;
opt.orientation = orientation();
// draw the section
style()->drawControl(QStyle::CE_Header, &opt, painter, this);
painter->setBrushOrigin(oldBO);
QRect newRect(rect.topLeft(), rect.bottomRight() - QPoint(35, 0));
QHeaderView::paintSection(painter, newRect, logicalIndex);
} else
QHeaderView::paintSection(painter, rect, logicalIndex);
}
void HeaderView::setModel(QAbstractItemModel* model)
{
Q_D(HeaderView);
for (int i = 0; i < d->showHideColumnsActionList.count(); ++i) {
delete d->showHideColumnsActionList.at(i);
}
clearSortIndicators();
d->showHideColumnsActionList.clear();
QHeaderView::setModel(model);
if (model) {
QAction *inserting;
for (int i = 0; i < model->columnCount(QModelIndex()); ++i) {
inserting = new QAction(model->headerData(i, orientation()).toString(), this);
inserting->setCheckable(true);
inserting->setChecked(true);
d->showHideColumnsActionList.append(inserting);
d->showHideColumnsMenu->addAction(inserting);
connect(inserting, SIGNAL(toggled(bool)),
this, SLOT(showHideAction_toggled(bool)));
}
checkOneColumn();
}
}
void HeaderView::headerDataChanged(Qt::Orientation orientation, int logicalFirst, int logicalLast)
{
Q_D(HeaderView);
for (int i = logicalFirst; i <= logicalLast; ++i)
d->showHideColumnsActionList.value(i)->setText(model()->headerData(i, orientation).toString());
QHeaderView::headerDataChanged(orientation, logicalFirst, logicalLast);
}
void HeaderView::setSectionHidden(int logicalIndex, bool hide)
{
Q_D(HeaderView);
if (logicalIndex >= 0 && logicalIndex < d->showHideColumnsActionList.count())
d->showHideColumnsActionList.value(logicalIndex)->setChecked(!hide);
checkOneColumn();
QHeaderView::setSectionHidden(logicalIndex, hide);
}
void HeaderView::hideSection(int logicalIndex)
{
setSectionHidden(logicalIndex, true);
}
void HeaderView::showSection(int logicalIndex)
{
setSectionHidden(logicalIndex, false);
}
void HeaderView::sectionsAboutToBeRemoved(const QModelIndex &parent, int logicalFirst, int logicalLast)
{
Q_D(HeaderView);
if (!parent.isValid()) {
for (int i = logicalFirst; i <= logicalLast; ++i) {
disconnect(d->showHideColumnsActionList.at(i), SIGNAL(toggled(bool)),
this, SLOT(showHideAction_toggled(bool)));
d->showHideColumnsActionList.removeAt(i);
}
}
checkOneColumn();
QHeaderView::sectionsAboutToBeRemoved(parent, logicalFirst, logicalLast);
}
void HeaderView::sectionsInserted(const QModelIndex& parent, int logicalFirst, int logicalLast)
{
Q_D(HeaderView);
if (!parent.isValid()) {
QAction *inserting;
for (int i = logicalFirst; i <= logicalLast; ++i) {
inserting = new QAction(model()->headerData(i, orientation()).toString(), this);
d->showHideColumnsActionList.insert(i, inserting);
connect(inserting, SIGNAL(toggled(bool)),
this, SLOT(showHideAction_toggled(bool)));
}
}
checkOneColumn();
QHeaderView::sectionsInserted(parent, logicalFirst, logicalLast);
}
void HeaderView::showHideAction_toggled(bool checked)
{
Q_D(HeaderView);
QAction *senderAction = qobject_cast<QAction *> (sender());
if (senderAction) {
QHeaderView::setSectionHidden(d->showHideColumnsActionList.indexOf(senderAction), !checked);
checkOneColumn();
}
}
void HeaderView::checkOneColumn()
{
Q_D(HeaderView);
if (hiddenSectionCount() + 1 == d->showHideColumnsActionList.count()) {
for (int i = 0; i < d->showHideColumnsActionList.count(); ++i)
if (d->showHideColumnsActionList.value(i)->isChecked()) {
d->showHideColumnsActionList.value(i)->setEnabled(false);
break;
}
} else {
for (int i = 0; i < d->showHideColumnsActionList.count(); ++i)
d->showHideColumnsActionList.value(i)->setEnabled(true);
}
}
Пример использования.
В унаследованном от QtableView классе в конструкторе
d->horizontalHeader = new HeaderView(Qt::Horizontal, this);
d->horizontalHeader->setClickable(true);
setHorizontalHeader(d->horizontalHeader);
d->horizontalHeader->setMovable(true);
В слоте привязанном к сигналу QHeaderView::clicked(int)
if (QApplication::keyboardModifiers() & Qt::ControlModifier)
{
if (d->horizontalHeader->sortIndicatorOrder(logicalIndex) == Qt::DescendingOrder) {
d->horizontalHeader->removeSortIndicator(logicalIndex);
d->model->removeSort(logicalIndex);
} else if (!d->horizontalHeader->isSortIndicatorShown(logicalIndex)) {
d->horizontalHeader->setSortIndicator(logicalIndex, Qt::AscendingOrder, false);
d->model->sort(logicalIndex, Qt::AscendingOrder, false);
} else {
d->horizontalHeader->setSortIndicator(logicalIndex, Qt::DescendingOrder, false);
d->model->sort(logicalIndex, Qt::DescendingOrder, false);
}
d->model->refresh();
//} else if (QApplication::keyboardModifiers() & Qt::ShiftModifier) {
} else if (d->model->hasFeature(AbstractTableModel::Sortable)) {
d->model->clearSort();
if (d->horizontalHeader->isSortIndicatorShown(logicalIndex)) {
if (d->horizontalHeader->sortIndicatorOrder(logicalIndex) == Qt::DescendingOrder) {
d->horizontalHeader->clearSortIndicators();
d->model->refresh();
} else {
if (d->horizontalHeader->sortIndicatorSections().count() > 1)
d->horizontalHeader->clearSortIndicators();
d->horizontalHeader->setSortIndicator(logicalIndex, Qt::DescendingOrder);
d->model->sort(logicalIndex, Qt::DescendingOrder);
}
} else {
if (d->horizontalHeader->sortIndicatorSections().count() > 1)
d->horizontalHeader->clearSortIndicators();
d->model->sort(logicalIndex, Qt::AscendingOrder);
d->horizontalHeader->setSortIndicator(logicalIndex, Qt::AscendingOrder);
}
d->model->refresh();
selectColumn(logicalIndex);
}