diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp index e20e964b9..08f1eba9c 100644 --- a/src/qt/peertablemodel.cpp +++ b/src/qt/peertablemodel.cpp @@ -1,233 +1,231 @@ // Copyright (c) 2011-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include -#include #include #include #include #include #include #include #include bool NodeLessThan::operator()(const CNodeCombinedStats &left, const CNodeCombinedStats &right) const { const CNodeStats *pLeft = &(left.nodeStats); const CNodeStats *pRight = &(right.nodeStats); if (order == Qt::DescendingOrder) { std::swap(pLeft, pRight); } switch (column) { case PeerTableModel::NetNodeId: return pLeft->nodeid < pRight->nodeid; case PeerTableModel::Address: return pLeft->addrName.compare(pRight->addrName) < 0; case PeerTableModel::Subversion: return pLeft->cleanSubVer.compare(pRight->cleanSubVer) < 0; case PeerTableModel::Ping: return pLeft->m_min_ping_usec < pRight->m_min_ping_usec; case PeerTableModel::Sent: return pLeft->nSendBytes < pRight->nSendBytes; case PeerTableModel::Received: return pLeft->nRecvBytes < pRight->nRecvBytes; } return false; } // private implementation class PeerTablePriv { public: /** Local cache of peer information */ QList cachedNodeStats; /** Column to sort nodes by (default to unsorted) */ int sortColumn{-1}; /** Order (ascending or descending) to sort nodes by */ Qt::SortOrder sortOrder; /** Index of rows by node ID */ std::map mapNodeRows; /** Pull a full list of peers from vNodes into our cache */ void refreshPeers(interfaces::Node &node) { { cachedNodeStats.clear(); interfaces::Node::NodesStats nodes_stats; node.getNodesStats(nodes_stats); cachedNodeStats.reserve(nodes_stats.size()); for (const auto &node_stats : nodes_stats) { CNodeCombinedStats stats; stats.nodeStats = std::get<0>(node_stats); stats.fNodeStateStatsAvailable = std::get<1>(node_stats); stats.nodeStateStats = std::get<2>(node_stats); cachedNodeStats.append(stats); } } if (sortColumn >= 0) { // sort cacheNodeStats (use stable sort to prevent rows jumping // around unnecessarily) std::stable_sort(cachedNodeStats.begin(), cachedNodeStats.end(), NodeLessThan(sortColumn, sortOrder)); } // build index map mapNodeRows.clear(); int row = 0; for (const CNodeCombinedStats &stats : cachedNodeStats) { mapNodeRows.insert( std::pair(stats.nodeStats.nodeid, row++)); } } int size() const { return cachedNodeStats.size(); } CNodeCombinedStats *index(int idx) { if (idx >= 0 && idx < cachedNodeStats.size()) { return &cachedNodeStats[idx]; } return nullptr; } }; -PeerTableModel::PeerTableModel(interfaces::Node &node, ClientModel *parent) - : QAbstractTableModel(parent), m_node(node), clientModel(parent), - timer(nullptr) { +PeerTableModel::PeerTableModel(interfaces::Node &node, QObject *parent) + : QAbstractTableModel(parent), m_node(node), timer(nullptr) { columns << tr("NodeId") << tr("Node/Service") << tr("Ping") << tr("Sent") << tr("Received") << tr("User Agent"); priv.reset(new PeerTablePriv()); // set up timer for auto refresh timer = new QTimer(this); connect(timer, &QTimer::timeout, this, &PeerTableModel::refresh); timer->setInterval(MODEL_UPDATE_DELAY); // load initial data refresh(); } PeerTableModel::~PeerTableModel() { // Intentionally left empty } void PeerTableModel::startAutoRefresh() { timer->start(); } void PeerTableModel::stopAutoRefresh() { timer->stop(); } int PeerTableModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); return priv->size(); } int PeerTableModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return columns.length(); } QVariant PeerTableModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) { return QVariant(); } CNodeCombinedStats *rec = static_cast(index.internalPointer()); if (role == Qt::DisplayRole) { switch (index.column()) { case NetNodeId: return (qint64)rec->nodeStats.nodeid; case Address: // prepend to peer address down-arrow symbol for inbound // connection and up-arrow for outbound connection return QString(rec->nodeStats.fInbound ? "↓ " : "↑ ") + QString::fromStdString(rec->nodeStats.addrName); case Subversion: return QString::fromStdString(rec->nodeStats.cleanSubVer); case Ping: return GUIUtil::formatPingTime(rec->nodeStats.m_min_ping_usec); case Sent: return GUIUtil::formatBytes(rec->nodeStats.nSendBytes); case Received: return GUIUtil::formatBytes(rec->nodeStats.nRecvBytes); } } else if (role == Qt::TextAlignmentRole) { switch (index.column()) { case Ping: case Sent: case Received: return QVariant(Qt::AlignRight | Qt::AlignVCenter); default: return QVariant(); } } return QVariant(); } QVariant PeerTableModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal) { if (role == Qt::DisplayRole && section < columns.size()) { return columns[section]; } } return QVariant(); } Qt::ItemFlags PeerTableModel::flags(const QModelIndex &index) const { if (!index.isValid()) { return Qt::NoItemFlags; } Qt::ItemFlags retval = Qt::ItemIsSelectable | Qt::ItemIsEnabled; return retval; } QModelIndex PeerTableModel::index(int row, int column, const QModelIndex &parent) const { Q_UNUSED(parent); CNodeCombinedStats *data = priv->index(row); if (data) { return createIndex(row, column, data); } return QModelIndex(); } const CNodeCombinedStats *PeerTableModel::getNodeStats(int idx) { return priv->index(idx); } void PeerTableModel::refresh() { Q_EMIT layoutAboutToBeChanged(); priv->refreshPeers(m_node); Q_EMIT layoutChanged(); } int PeerTableModel::getRowByNodeId(NodeId nodeid) { std::map::iterator it = priv->mapNodeRows.find(nodeid); if (it == priv->mapNodeRows.end()) { return -1; } return it->second; } void PeerTableModel::sort(int column, Qt::SortOrder order) { priv->sortColumn = column; priv->sortOrder = order; refresh(); } diff --git a/src/qt/peertablemodel.h b/src/qt/peertablemodel.h index fb88f3355..e3eacd0a1 100644 --- a/src/qt/peertablemodel.h +++ b/src/qt/peertablemodel.h @@ -1,94 +1,91 @@ // Copyright (c) 2011-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_QT_PEERTABLEMODEL_H #define BITCOIN_QT_PEERTABLEMODEL_H #include #include // For CNodeStateStats #include #include #include -class ClientModel; class PeerTablePriv; namespace interfaces { class Node; } QT_BEGIN_NAMESPACE class QTimer; QT_END_NAMESPACE struct CNodeCombinedStats { CNodeStats nodeStats; CNodeStateStats nodeStateStats; bool fNodeStateStatsAvailable; }; class NodeLessThan { public: NodeLessThan(int nColumn, Qt::SortOrder fOrder) : column(nColumn), order(fOrder) {} bool operator()(const CNodeCombinedStats &left, const CNodeCombinedStats &right) const; private: int column; Qt::SortOrder order; }; /** * Qt model providing information about connected peers, similar to the * "getpeerinfo" RPC call. Used by the rpc console UI. */ class PeerTableModel : public QAbstractTableModel { Q_OBJECT public: - explicit PeerTableModel(interfaces::Node &node, - ClientModel *parent = nullptr); + explicit PeerTableModel(interfaces::Node &node, QObject *parent); ~PeerTableModel(); const CNodeCombinedStats *getNodeStats(int idx); int getRowByNodeId(NodeId nodeid); void startAutoRefresh(); void stopAutoRefresh(); enum ColumnIndex { NetNodeId = 0, Address = 1, Ping = 2, Sent = 3, Received = 4, Subversion = 5, }; /** @name Methods overridden from QAbstractTableModel @{*/ int rowCount(const QModelIndex &parent) const override; int columnCount(const QModelIndex &parent) const override; QVariant data(const QModelIndex &index, int role) const override; QVariant headerData(int section, Qt::Orientation orientation, int role) const override; QModelIndex index(int row, int column, const QModelIndex &parent) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; void sort(int column, Qt::SortOrder order) override; /*@}*/ public Q_SLOTS: void refresh(); private: interfaces::Node &m_node; - ClientModel *clientModel; QStringList columns; std::unique_ptr priv; QTimer *timer; }; #endif // BITCOIN_QT_PEERTABLEMODEL_H diff --git a/test/lint/lint-circular-dependencies.sh b/test/lint/lint-circular-dependencies.sh index cca6bc26d..debfc3753 100755 --- a/test/lint/lint-circular-dependencies.sh +++ b/test/lint/lint-circular-dependencies.sh @@ -1,81 +1,80 @@ #!/usr/bin/env bash # # Copyright (c) 2018 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. # # Check for circular dependencies export LC_ALL=C set -euo pipefail : "${TOPLEVEL:=$(git rev-parse --show-toplevel)}" EXPECTED_CIRCULAR_DEPENDENCIES=( "index/txindex -> validation -> index/txindex" "qt/addresstablemodel -> qt/walletmodel -> qt/addresstablemodel" "qt/bitcoingui -> qt/walletframe -> qt/bitcoingui" - "qt/clientmodel -> qt/peertablemodel -> qt/clientmodel" "qt/recentrequeststablemodel -> qt/walletmodel -> qt/recentrequeststablemodel" "qt/transactiontablemodel -> qt/walletmodel -> qt/transactiontablemodel" "txmempool -> validation -> txmempool" "wallet/fees -> wallet/wallet -> wallet/fees" "wallet/rpcwallet -> wallet/wallet -> wallet/rpcwallet" "wallet/wallet -> wallet/walletdb -> wallet/wallet" "avalanche/processor -> validation -> avalanche/processor" "avalanche/processor -> net_processing -> avalanche/processor" "chainparams -> protocol -> chainparams" "chainparamsbase -> util/system -> chainparamsbase" "minerfund -> validation -> minerfund" "script/scriptcache -> validation -> script/scriptcache" "seeder/bitcoin -> seeder/db -> seeder/bitcoin" "chainparams -> protocol -> config -> chainparams" "checkpoints -> validation -> checkpoints" "pow/aserti32d -> validation -> pow/aserti32d" "pow/aserti32d -> validation -> pow/pow -> pow/aserti32d" ) EXIT_CODE=0 CIRCULAR_DEPENDENCIES=() pushd "${TOPLEVEL}" IFS=$'\n' for CIRC in $(cd src && ../contrib/devtools/circular-dependencies.py {*,*/*,*/*/*}.{h,cpp} | sed -e 's/^Circular dependency: //'); do CIRCULAR_DEPENDENCIES+=("$CIRC") IS_EXPECTED_CIRC=0 for EXPECTED_CIRC in "${EXPECTED_CIRCULAR_DEPENDENCIES[@]}"; do if [[ "${CIRC}" == "${EXPECTED_CIRC}" ]]; then IS_EXPECTED_CIRC=1 break fi done if [[ ${IS_EXPECTED_CIRC} == 0 ]]; then echo "A new circular dependency in the form of \"${CIRC}\" appears to have been introduced." echo EXIT_CODE=1 fi done for EXPECTED_CIRC in "${EXPECTED_CIRCULAR_DEPENDENCIES[@]}"; do IS_PRESENT_EXPECTED_CIRC=0 for CIRC in "${CIRCULAR_DEPENDENCIES[@]}"; do if [[ "${CIRC}" == "${EXPECTED_CIRC}" ]]; then IS_PRESENT_EXPECTED_CIRC=1 break fi done if [[ ${IS_PRESENT_EXPECTED_CIRC} == 0 ]]; then echo "Good job! The circular dependency \"${EXPECTED_CIRC}\" is no longer present." echo "Please remove it from EXPECTED_CIRCULAR_DEPENDENCIES in $0" echo "to make sure this circular dependency is not accidentally reintroduced." echo EXIT_CODE=1 fi done popd exit ${EXIT_CODE}