sukhmel
sukhmel

Reputation: 1492

Restore original order in QTableView/QSortFilterProxy

I have a QTableView with QSortFilterProxyModel between the view and the model (QStandardItemModel). The problem is when I call sort() I'm unable to restore original order of lines in a table. I was trying to acheve that by changing model proxy to QIdentityProxy on-the-fly but to no avail, as the only change is that lines are renumbered but order is kept sorted.

Is it possible to somehow "unsort" data? I think, that code is unnecessary in this case, but will post if asked.

I'm using Qt5 on Win x64

P.S.: The same problem was posted here back in 2009 but never was answered.

Upvotes: 6

Views: 7307

Answers (7)

one interesting fact:

if you have your source model being sorted by more than one criteria for example QSqlQuerModel *source_model where the query contians "ORDER BY familyName, Name" than setting sort(-1) will not be enough

you will have to disable sortingEnabled property of the QTableView that is using this model to revert to sorting by 2 criterias

Upvotes: 1

s0mbre
s0mbre

Reputation: 574

From the docs:

QSortFilterProxyModel can be sorted by column -1, in which case it returns to the sort order of the underlying source model.

So, as @eTHP says, sort(-1) does the trick!

Upvotes: 0

eTHP
eTHP

Reputation: 81

This is what worked best for me:

QSortFilterProxyModel *proxyModel = myTableView->model ();
proxyModel->sort (-1);

Note that, this won't update the header indicator of a TableView, so before calling the code above, I'm also calling:

myTableView->horizontalHeader ()->setSortIndicator (0, Qt::DescendingOrder);

I don't really like it, but I haven't found a way of using the TableView or HorizontalHeader to reset the QSortFilterProxyModel sorting.

This is the full code:

// Block QHeaderView signals so sorting doesn't happen twice
myTableView->horizontalHeader ()->blockSignals (true);

// Update the sort indicator to be the same as it was when the TableView was created
myTableView->horizontalHeader ()->setSortIndicator (0, Qt::DescendingOrder);

// Reset sorting
QSortFilterProxyModel *proxyModel = myTableView->model ();
proxyModel->sort (-1);

// Unblock QHeaderView signals
myTableView->horizontalHeader ()->blockSignals (false);

NOTE: I'm blocking the horizontal header signals temporarily to prevent QSortFilterProxyModel sort from executing twice.

Upvotes: 2

panda-34
panda-34

Reputation: 4219

I like to use top-left corner button to restore order (that is, sort by row number to which that button is the header). This works with standard classes, in pyqt 5.9:

def __init__(self):
    #...
    tb = self.tableView # has sorting enabled and SortIndicator shown
    proxy = QSortFilterProxyModel(self)
    proxy.setSourceModel(self.model)
    tb.setModel(proxy)
    btn = tb.findChild(QAbstractButton)
    if btn:
        btn.disconnect()
        btn.clicked.connect(self.disableSorting)
    tb.horizontalHeader().setSortIndicator(-1, 0)

def disableSorting(self):
    self.tableView.model().sort(-1)
    self.tableView.horizontalHeader().setSortIndicator(-1, 0)

Upvotes: 2

fbucek
fbucek

Reputation: 1667

To restore initial unsorted state ( tested )

    sortModel->setSortRole(Qt::InitialSortOrderRole);
    sortModel->invalidate();

QSortFilterProxyModel::​setSortRole(int role)

Upvotes: 5

sukhmel
sukhmel

Reputation: 1492

The point is to sort manually deciding between sort by column -1 (restore) and normal column number, and intercept communication between QHeaderView and QSortFilterProxyModel somehow.

So, using some insight from @vahancho's answer, I've managed to implement sorting like this:

class ProxyModel : public QSortFilterProxyModel
{
    Q_OBJECT
public:
    ProxyModel(QObject* parent = 0);

signals:
    void askOrder(int column, Qt::SortOrder order);

public slots:
    //! override of automatically called function
    void sort(int column, Qt::SortOrder order) 
    { 
        emit askOrder(column, order); 
    }

    //! real sorting happens here
    void doSort(int column, Qt::SortOrder order) 
    { 
        QSortFilterProxyModel::sort(column, order); 
    }
};

and on parent's side I made proper connection and checks:

ResultsTable::ResultsTable(QWidget *parent) : QTableView(parent)
{
    /*...*/
    p_Header = new QHeaderView(this);
    p_Sort = new ProxyModel(this);
    connect(this, &ResultsTable::doSort, p_Sort, &ProxyModel::doSort);
    connect(p_Sort, &ProxyModel::askOrder, this, &ResultsTable::setSorting);
    /*...*/
    setSortingEnabled(true);
}

void ResultsTable::setSorting(int column, Qt::SortOrder order)
{
    if (p_Header->sortIndicatorOrder() == Qt::AscendingOrder && p_Header->isSortIndicatorShown() && m_PreviousSort == column)
    {
        p_Header->setSortIndicator(column, Qt::DescendingOrder);
        p_Header->setSortIndicatorShown(false);
        column = -1;
    }
    else
    {
        p_Header->setSortIndicatorShown(true);
    }
    m_PreviousSort = column;
    emit doSort(column, order);
}

this way I can use automatic sorting treating done by QTableView when sortingEnabled is true. I've tried to research what happens inside of Qt when table header is clicked to induce sorting, but failed, so stopped with this solution.

I'm still unsure if it's right that this way QTableView is responsible for setting correct sort indication, and not QHeaderView itself (as I thought this functionality should to belong to the header).

Upvotes: 4

vahancho
vahancho

Reputation: 21258

My understanding is that you need to return your sorting to its default state? What if you override the QSortFilterProxyModel::lessThan() function in the way that it returns the default value when you want to reset sorting, i.e.:

return QSortFilterProxyModel::lessThan(left, right);

,and custom sorting results when sorting "enabled"? I think you will also need to reset your proxy model to its original state with QAbstractItemModel::reset(). However, it will repopulate the whole model data and you will lost selection information.

Upvotes: 2

Related Questions