Skip to content

Commit 328da33

Browse files
committed
Merge bitcoin-core#18: Add peertablesortproxy module
5a4a15d qt, refactor: Drop no longer used PeerTableModel::getRowByNodeId func (Hennadii Stepanov) 9a9f180 qt, refactor: Drop no longer used PeerTableModel::sort function (Hennadii Stepanov) 778a64a qt: Use PeerTableSortProxy for sorting peer table (Hennadii Stepanov) df2d165 qt: Add peertablesortproxy module (Hennadii Stepanov) Pull request description: The "Peers" table in the "Node" window does not hold multiple selection after sorting. This PR introduces a `QSortFilterProxyModel` subclass, that is a standard Qt [practice](https://doc.qt.io/qt-5/model-view-programming.html#custom-sorting-models) for such cases. Now the sorting code is encapsulated into the dedicated Qt class, and we do not need to maintain it. Fixes bitcoin-core#283 (additionally). --- On **master** (7ae86b3): - rows are sorted by "Ping", and a selection is made ![Screenshot from 2020-11-28 22-53-11](https://user-images.githubusercontent.com/32963518/100525900-96eaed00-31cc-11eb-86e7-72ede3b8b33c.png) - rows are sorted by "NodeId", and the previous selection is _lost_ ![Screenshot from 2020-11-28 22-53-21](https://user-images.githubusercontent.com/32963518/100525904-9c483780-31cc-11eb-957c-06f53d7d31ab.png) With **this PR**: - rows are sorted by "Ping", and a selection is made ![Screenshot from 2020-11-28 22-39-41](https://user-images.githubusercontent.com/32963518/100525776-06aca800-31cc-11eb-8c4e-9c6566fe80fe.png) - rows are sorted by "NodeId", and the row are still selected ![Screenshot from 2020-11-28 22-39-53](https://user-images.githubusercontent.com/32963518/100525791-2348e000-31cc-11eb-8b78-716a5551d7ec.png) ACKs for top commit: jarolrod: re-ACK 5a4a15d, tested on macOS 11.2 Qt 5.15.2 after rebase promag: Tested ACK 5a4a15d. Tree-SHA512: f81c1385892fbf1a46ffb98b42094ca1cc97da52114bbbc94fedb553899b1f18c26a349e186bba6e27922a89426bd61e8bc88b1f7832512dbe211b5f834e076e
2 parents 549d20a + 5a4a15d commit 328da33

File tree

10 files changed

+93
-155
lines changed

10 files changed

+93
-155
lines changed

build_msvc/libbitcoin_qt/libbitcoin_qt.vcxproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
<ClCompile Include="..\..\src\qt\overviewpage.cpp" />
3535
<ClCompile Include="..\..\src\qt\paymentserver.cpp" />
3636
<ClCompile Include="..\..\src\qt\peertablemodel.cpp" />
37+
<ClCompile Include="..\..\src\qt\peertablesortproxy.cpp" />
3738
<ClCompile Include="..\..\src\qt\platformstyle.cpp" />
3839
<ClCompile Include="..\..\src\qt\psbtoperationsdialog.cpp" />
3940
<ClCompile Include="..\..\src\qt\qrimagewidget.cpp" />
@@ -87,6 +88,7 @@
8788
<ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_overviewpage.cpp" />
8889
<ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_paymentserver.cpp" />
8990
<ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_peertablemodel.cpp" />
91+
<ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_peertablesortproxy.cpp" />
9092
<ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_platformstyle.cpp" />
9193
<ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_psbtoperationsdialog.cpp" />
9294
<ClCompile Include="$(GeneratedFilesOutDir)\moc\moc_qrimagewidget.cpp" />

src/Makefile.qt.include

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ QT_MOC_CPP = \
6161
qt/moc_optionsmodel.cpp \
6262
qt/moc_overviewpage.cpp \
6363
qt/moc_peertablemodel.cpp \
64+
qt/moc_peertablesortproxy.cpp \
6465
qt/moc_paymentserver.cpp \
6566
qt/moc_psbtoperationsdialog.cpp \
6667
qt/moc_qrimagewidget.cpp \
@@ -134,6 +135,7 @@ BITCOIN_QT_H = \
134135
qt/overviewpage.h \
135136
qt/paymentserver.h \
136137
qt/peertablemodel.h \
138+
qt/peertablesortproxy.h \
137139
qt/platformstyle.h \
138140
qt/psbtoperationsdialog.h \
139141
qt/qrimagewidget.h \
@@ -232,6 +234,7 @@ BITCOIN_QT_BASE_CPP = \
232234
qt/optionsdialog.cpp \
233235
qt/optionsmodel.cpp \
234236
qt/peertablemodel.cpp \
237+
qt/peertablesortproxy.cpp \
235238
qt/platformstyle.cpp \
236239
qt/qvalidatedlineedit.cpp \
237240
qt/qvaluecombobox.cpp \

src/qt/clientmodel.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <qt/guiconstants.h>
99
#include <qt/guiutil.h>
1010
#include <qt/peertablemodel.h>
11+
#include <qt/peertablesortproxy.h>
1112

1213
#include <clientversion.h>
1314
#include <interfaces/handler.h>
@@ -38,7 +39,11 @@ ClientModel::ClientModel(interfaces::Node& node, OptionsModel *_optionsModel, QO
3839
{
3940
cachedBestHeaderHeight = -1;
4041
cachedBestHeaderTime = -1;
42+
4143
peerTableModel = new PeerTableModel(m_node, this);
44+
m_peer_table_sort_proxy = new PeerTableSortProxy(this);
45+
m_peer_table_sort_proxy->setSourceModel(peerTableModel);
46+
4247
banTableModel = new BanTableModel(m_node, this);
4348

4449
QTimer* timer = new QTimer;
@@ -184,6 +189,11 @@ PeerTableModel *ClientModel::getPeerTableModel()
184189
return peerTableModel;
185190
}
186191

192+
PeerTableSortProxy* ClientModel::peerTableSortProxy()
193+
{
194+
return m_peer_table_sort_proxy;
195+
}
196+
187197
BanTableModel *ClientModel::getBanTableModel()
188198
{
189199
return banTableModel;

src/qt/clientmodel.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ class BanTableModel;
1717
class CBlockIndex;
1818
class OptionsModel;
1919
class PeerTableModel;
20+
class PeerTableSortProxy;
2021
enum class SynchronizationState;
2122

2223
namespace interfaces {
@@ -54,6 +55,7 @@ class ClientModel : public QObject
5455
interfaces::Node& node() const { return m_node; }
5556
OptionsModel *getOptionsModel();
5657
PeerTableModel *getPeerTableModel();
58+
PeerTableSortProxy* peerTableSortProxy();
5759
BanTableModel *getBanTableModel();
5860

5961
//! Return number of connections, default is in- and outbound (total)
@@ -96,6 +98,7 @@ class ClientModel : public QObject
9698
std::unique_ptr<interfaces::Handler> m_handler_notify_header_tip;
9799
OptionsModel *optionsModel;
98100
PeerTableModel *peerTableModel;
101+
PeerTableSortProxy* m_peer_table_sort_proxy{nullptr};
99102
BanTableModel *banTableModel;
100103

101104
//! A thread to interact with m_node asynchronously

src/qt/peertablemodel.cpp

Lines changed: 1 addition & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -11,56 +11,19 @@
1111

1212
#include <utility>
1313

14-
#include <QDebug>
1514
#include <QList>
1615
#include <QTimer>
1716

18-
bool NodeLessThan::operator()(const CNodeCombinedStats &left, const CNodeCombinedStats &right) const
19-
{
20-
const CNodeStats *pLeft = &(left.nodeStats);
21-
const CNodeStats *pRight = &(right.nodeStats);
22-
23-
if (order == Qt::DescendingOrder)
24-
std::swap(pLeft, pRight);
25-
26-
switch (static_cast<PeerTableModel::ColumnIndex>(column)) {
27-
case PeerTableModel::NetNodeId:
28-
return pLeft->nodeid < pRight->nodeid;
29-
case PeerTableModel::Address:
30-
return pLeft->addrName.compare(pRight->addrName) < 0;
31-
case PeerTableModel::ConnectionType:
32-
return pLeft->m_conn_type < pRight->m_conn_type;
33-
case PeerTableModel::Network:
34-
return pLeft->m_network < pRight->m_network;
35-
case PeerTableModel::Ping:
36-
return pLeft->m_min_ping_time < pRight->m_min_ping_time;
37-
case PeerTableModel::Sent:
38-
return pLeft->nSendBytes < pRight->nSendBytes;
39-
case PeerTableModel::Received:
40-
return pLeft->nRecvBytes < pRight->nRecvBytes;
41-
case PeerTableModel::Subversion:
42-
return pLeft->cleanSubVer.compare(pRight->cleanSubVer) < 0;
43-
} // no default case, so the compiler can warn about missing cases
44-
assert(false);
45-
}
46-
4717
// private implementation
4818
class PeerTablePriv
4919
{
5020
public:
5121
/** Local cache of peer information */
5222
QList<CNodeCombinedStats> cachedNodeStats;
53-
/** Column to sort nodes by (default to unsorted) */
54-
int sortColumn{-1};
55-
/** Order (ascending or descending) to sort nodes by */
56-
Qt::SortOrder sortOrder;
57-
/** Index of rows by node ID */
58-
std::map<NodeId, int> mapNodeRows;
5923

6024
/** Pull a full list of peers from vNodes into our cache */
6125
void refreshPeers(interfaces::Node& node)
6226
{
63-
{
6427
cachedNodeStats.clear();
6528

6629
interfaces::Node::NodesStats nodes_stats;
@@ -74,17 +37,6 @@ class PeerTablePriv
7437
stats.nodeStateStats = std::get<2>(node_stats);
7538
cachedNodeStats.append(stats);
7639
}
77-
}
78-
79-
if (sortColumn >= 0)
80-
// sort cacheNodeStats (use stable sort to prevent rows jumping around unnecessarily)
81-
std::stable_sort(cachedNodeStats.begin(), cachedNodeStats.end(), NodeLessThan(sortColumn, sortOrder));
82-
83-
// build index map
84-
mapNodeRows.clear();
85-
int row = 0;
86-
for (const CNodeCombinedStats& stats : cachedNodeStats)
87-
mapNodeRows.insert(std::pair<NodeId, int>(stats.nodeStats.nodeid, row++));
8840
}
8941

9042
int size() const
@@ -194,10 +146,7 @@ QVariant PeerTableModel::data(const QModelIndex &index, int role) const
194146
} // no default case, so the compiler can warn about missing cases
195147
assert(false);
196148
} else if (role == StatsRole) {
197-
switch (index.column()) {
198-
case NetNodeId: return QVariant::fromValue(rec);
199-
default: return QVariant();
200-
}
149+
return QVariant::fromValue(rec);
201150
}
202151

203152
return QVariant();
@@ -239,19 +188,3 @@ void PeerTableModel::refresh()
239188
priv->refreshPeers(m_node);
240189
Q_EMIT layoutChanged();
241190
}
242-
243-
int PeerTableModel::getRowByNodeId(NodeId nodeid)
244-
{
245-
std::map<NodeId, int>::iterator it = priv->mapNodeRows.find(nodeid);
246-
if (it == priv->mapNodeRows.end())
247-
return -1;
248-
249-
return it->second;
250-
}
251-
252-
void PeerTableModel::sort(int column, Qt::SortOrder order)
253-
{
254-
priv->sortColumn = column;
255-
priv->sortOrder = order;
256-
refresh();
257-
}

src/qt/peertablemodel.h

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,18 +30,6 @@ struct CNodeCombinedStats {
3030
};
3131
Q_DECLARE_METATYPE(CNodeCombinedStats*)
3232

33-
class NodeLessThan
34-
{
35-
public:
36-
NodeLessThan(int nColumn, Qt::SortOrder fOrder) :
37-
column(nColumn), order(fOrder) {}
38-
bool operator()(const CNodeCombinedStats &left, const CNodeCombinedStats &right) const;
39-
40-
private:
41-
int column;
42-
Qt::SortOrder order;
43-
};
44-
4533
/**
4634
Qt model providing information about connected peers, similar to the
4735
"getpeerinfo" RPC call. Used by the rpc console UI.
@@ -53,7 +41,6 @@ class PeerTableModel : public QAbstractTableModel
5341
public:
5442
explicit PeerTableModel(interfaces::Node& node, QObject* parent);
5543
~PeerTableModel();
56-
int getRowByNodeId(NodeId nodeid);
5744
void startAutoRefresh();
5845
void stopAutoRefresh();
5946

@@ -80,7 +67,6 @@ class PeerTableModel : public QAbstractTableModel
8067
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
8168
QModelIndex index(int row, int column, const QModelIndex &parent) const override;
8269
Qt::ItemFlags flags(const QModelIndex &index) const override;
83-
void sort(int column, Qt::SortOrder order) override;
8470
/*@}*/
8571

8672
public Q_SLOTS:

src/qt/peertablesortproxy.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright (c) 2020 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#include <qt/peertablesortproxy.h>
6+
7+
#include <qt/peertablemodel.h>
8+
#include <util/check.h>
9+
10+
#include <QModelIndex>
11+
#include <QString>
12+
#include <QVariant>
13+
14+
PeerTableSortProxy::PeerTableSortProxy(QObject* parent)
15+
: QSortFilterProxyModel(parent)
16+
{
17+
}
18+
19+
bool PeerTableSortProxy::lessThan(const QModelIndex& left_index, const QModelIndex& right_index) const
20+
{
21+
const CNodeStats left_stats = Assert(sourceModel()->data(left_index, PeerTableModel::StatsRole).value<CNodeCombinedStats*>())->nodeStats;
22+
const CNodeStats right_stats = Assert(sourceModel()->data(right_index, PeerTableModel::StatsRole).value<CNodeCombinedStats*>())->nodeStats;
23+
24+
switch (static_cast<PeerTableModel::ColumnIndex>(left_index.column())) {
25+
case PeerTableModel::NetNodeId:
26+
return left_stats.nodeid < right_stats.nodeid;
27+
case PeerTableModel::Address:
28+
return left_stats.addrName.compare(right_stats.addrName) < 0;
29+
case PeerTableModel::ConnectionType:
30+
return left_stats.m_conn_type < right_stats.m_conn_type;
31+
case PeerTableModel::Network:
32+
return left_stats.m_network < right_stats.m_network;
33+
case PeerTableModel::Ping:
34+
return left_stats.m_min_ping_time < right_stats.m_min_ping_time;
35+
case PeerTableModel::Sent:
36+
return left_stats.nSendBytes < right_stats.nSendBytes;
37+
case PeerTableModel::Received:
38+
return left_stats.nRecvBytes < right_stats.nRecvBytes;
39+
case PeerTableModel::Subversion:
40+
return left_stats.cleanSubVer.compare(right_stats.cleanSubVer) < 0;
41+
} // no default case, so the compiler can warn about missing cases
42+
assert(false);
43+
}

src/qt/peertablesortproxy.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright (c) 2020 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#ifndef BITCOIN_QT_PEERTABLESORTPROXY_H
6+
#define BITCOIN_QT_PEERTABLESORTPROXY_H
7+
8+
#include <QSortFilterProxyModel>
9+
10+
QT_BEGIN_NAMESPACE
11+
class QModelIndex;
12+
QT_END_NAMESPACE
13+
14+
class PeerTableSortProxy : public QSortFilterProxyModel
15+
{
16+
Q_OBJECT
17+
18+
public:
19+
explicit PeerTableSortProxy(QObject* parent = nullptr);
20+
21+
protected:
22+
bool lessThan(const QModelIndex& left_index, const QModelIndex& right_index) const override;
23+
};
24+
25+
#endif // BITCOIN_QT_PEERTABLESORTPROXY_H

0 commit comments

Comments
 (0)