在QTableView/QSortFilterProxy中恢复原始顺序

5
我有一个带有QSortFilterProxyModelQTableView,它位于视图和模型(QStandardItemModel)之间。问题是当我调用sort()时,我无法恢复表格中行的原始顺序。我试图通过在运行时将模型代理更改为QIdentityProxy来实现这一点,但无济于事,因为唯一的变化是行被重新编号,但顺序仍然保持排序。

有没有可能以某种方式“取消排序”数据?我认为,在这种情况下,代码是不必要的,但如果需要,我会发布。

我正在使用Win x64上的Qt5。

P.S.:同样的问题here曾在2009年发布过,但从未得到解答。

7个回答

5

1
这个可以用,但是之后会完全禁用按列排序。 - phyatt
我认为最好使用Qt :: DisplayRole而不是Qt :: InitialSortOrderRole,因为这是默认值。 - AshkanVZ

3
关键在于手动排序,需要决定是否按列-1(还原)或普通列号进行排序,并在某种程度上拦截QHeaderViewQSortFilterProxyModel之间的通信。

因此,借鉴了@vahancho答案中的一些见解,我已经成功实现了如下排序:

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); 
    }
};

在父级的一侧,我进行了适当的连接和检查:

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);
}

这样做可以利用QTableView自动排序,前提是sortingEnabled属性设置为true。我曾试图研究Qt中单击表头触发排序的内部处理过程,但未能成功,因此采用了这种方法。我仍然不确定这种方式是否正确,即QTableView负责设置正确的排序指示,而不是QHeaderView本身(我认为这个功能应该属于表头)。

这太棒了!它使每个标题看起来像三态按钮,视图的默认状态是未排序的。 - phyatt
1
理想情况下,这应该作为一个选项内置到Qt中,以允许三态排序循环。不过这是个不错的解决方法。 - Patrick Parker

2

我喜欢使用左上角的按钮来恢复顺序(也就是按照该按钮所在行号排序)。这适用于标准类,在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)

2
这是我觉得最好的方法:

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

请注意,这不会更新TableView的标题指示器,因此在调用上述代码之前,我还会调用以下代码:
myTableView->horizontalHeader ()->setSortIndicator (0, Qt::DescendingOrder);

我不是很喜欢它,但我还没有找到使用TableView或HorizontalHeader重置QSortFilterProxyModel排序的方法。

以下是完整代码:

// 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);

注意: 我暂时阻止水平标题信号,以防止 QSortFilterProxyModel 排序执行两次。


2

我的理解是你需要将排序恢复到默认状态?如果你重写QSortFilterProxyModel::lessThan()函数,让它在需要重置排序时返回默认值,会怎样呢?

return QSortFilterProxyModel::lessThan(left, right);

当对“enabled”进行排序时,您是否需要自定义排序结果?我认为您还需要使用QAbstractItemModel::reset()将代理模型重置为其原始状态。但是,这会重新填充整个模型数据,您将失去选择信息。


你的理解是正确的。顺便说一下,我现在对维护选择不是很感兴趣。我会立即尝试。 - sukhmel
这个想法很好,但是实现自定义的lessThan只有在填充表格时有助于保持顺序(总比没有好)。排序完成后它就没有任何效果了。 - sukhmel
@Idji,你尝试过在排序完成后重置模型吗?在重置模型之前,您需要在lessThan函数中禁用排序-这是我的想法。 - vahancho
当切换到fixed lessThen(...)时,我尝试使用resetInternalData()。接下来我要尝试的是使用模型的lessThan进行排序(如果它不为空,因为我还没有去电脑上检查所有内容)。 - sukhmel
2
顺便提一下,我应该更加关注文档中关于排序的说明:QSortFilterProxyModel可以按列-1进行排序,此时它返回底层源模型的排序顺序。现在以这种方式排序已经恢复了,尽管我似乎尝试过它但没有效果。 - sukhmel
显示剩余2条评论

1
一个有趣的事实:
如果您的源模型按多个条件排序,例如QSqlQueryModel *source_model,其中查询包含"ORDER BY familyName, Name",那么设置sort(-1)是不够的。
您需要禁用使用此模型的QTableView的sortingEnabled属性,以便恢复按照两个条件进行排序。

0

来自文档:

QSortFilterProxyModel 可以按列 -1 进行排序,这样它就会返回基础源模型的排序顺序。

因此,正如 @eTHP 所说,sort(-1) 就可以解决问题!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接