Merge pull request #4225
65f78a1
Qt: Add GUI view of peer information. #4133 (Ashley Holman)
This commit is contained in:
commit
4c097f9669
10 changed files with 870 additions and 12 deletions
|
@ -126,6 +126,7 @@ QT_MOC_CPP = \
|
||||||
moc_optionsdialog.cpp \
|
moc_optionsdialog.cpp \
|
||||||
moc_optionsmodel.cpp \
|
moc_optionsmodel.cpp \
|
||||||
moc_overviewpage.cpp \
|
moc_overviewpage.cpp \
|
||||||
|
moc_peertablemodel.cpp \
|
||||||
moc_paymentserver.cpp \
|
moc_paymentserver.cpp \
|
||||||
moc_qvalidatedlineedit.cpp \
|
moc_qvalidatedlineedit.cpp \
|
||||||
moc_qvaluecombobox.cpp \
|
moc_qvaluecombobox.cpp \
|
||||||
|
@ -191,6 +192,7 @@ BITCOIN_QT_H = \
|
||||||
overviewpage.h \
|
overviewpage.h \
|
||||||
paymentrequestplus.h \
|
paymentrequestplus.h \
|
||||||
paymentserver.h \
|
paymentserver.h \
|
||||||
|
peertablemodel.h \
|
||||||
qvalidatedlineedit.h \
|
qvalidatedlineedit.h \
|
||||||
qvaluecombobox.h \
|
qvaluecombobox.h \
|
||||||
receivecoinsdialog.h \
|
receivecoinsdialog.h \
|
||||||
|
@ -294,6 +296,7 @@ BITCOIN_QT_CPP += \
|
||||||
overviewpage.cpp \
|
overviewpage.cpp \
|
||||||
paymentrequestplus.cpp \
|
paymentrequestplus.cpp \
|
||||||
paymentserver.cpp \
|
paymentserver.cpp \
|
||||||
|
peertablemodel.cpp \
|
||||||
receivecoinsdialog.cpp \
|
receivecoinsdialog.cpp \
|
||||||
receiverequestdialog.cpp \
|
receiverequestdialog.cpp \
|
||||||
recentrequeststablemodel.cpp \
|
recentrequeststablemodel.cpp \
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include "clientmodel.h"
|
#include "clientmodel.h"
|
||||||
|
|
||||||
#include "guiconstants.h"
|
#include "guiconstants.h"
|
||||||
|
#include "peertablemodel.h"
|
||||||
|
|
||||||
#include "alert.h"
|
#include "alert.h"
|
||||||
#include "chainparams.h"
|
#include "chainparams.h"
|
||||||
|
@ -22,11 +23,12 @@
|
||||||
static const int64_t nClientStartupTime = GetTime();
|
static const int64_t nClientStartupTime = GetTime();
|
||||||
|
|
||||||
ClientModel::ClientModel(OptionsModel *optionsModel, QObject *parent) :
|
ClientModel::ClientModel(OptionsModel *optionsModel, QObject *parent) :
|
||||||
QObject(parent), optionsModel(optionsModel),
|
QObject(parent), optionsModel(optionsModel), peerTableModel(0),
|
||||||
cachedNumBlocks(0),
|
cachedNumBlocks(0),
|
||||||
cachedReindexing(0), cachedImporting(0),
|
cachedReindexing(0), cachedImporting(0),
|
||||||
numBlocksAtStartup(-1), pollTimer(0)
|
numBlocksAtStartup(-1), pollTimer(0)
|
||||||
{
|
{
|
||||||
|
peerTableModel = new PeerTableModel(this);
|
||||||
pollTimer = new QTimer(this);
|
pollTimer = new QTimer(this);
|
||||||
connect(pollTimer, SIGNAL(timeout()), this, SLOT(updateTimer()));
|
connect(pollTimer, SIGNAL(timeout()), this, SLOT(updateTimer()));
|
||||||
pollTimer->start(MODEL_UPDATE_DELAY);
|
pollTimer->start(MODEL_UPDATE_DELAY);
|
||||||
|
@ -173,6 +175,11 @@ OptionsModel *ClientModel::getOptionsModel()
|
||||||
return optionsModel;
|
return optionsModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PeerTableModel *ClientModel::getPeerTableModel()
|
||||||
|
{
|
||||||
|
return peerTableModel;
|
||||||
|
}
|
||||||
|
|
||||||
QString ClientModel::formatFullVersion() const
|
QString ClientModel::formatFullVersion() const
|
||||||
{
|
{
|
||||||
return QString::fromStdString(FormatFullVersion());
|
return QString::fromStdString(FormatFullVersion());
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
class AddressTableModel;
|
class AddressTableModel;
|
||||||
class OptionsModel;
|
class OptionsModel;
|
||||||
|
class PeerTableModel;
|
||||||
class TransactionTableModel;
|
class TransactionTableModel;
|
||||||
|
|
||||||
class CWallet;
|
class CWallet;
|
||||||
|
@ -42,6 +43,7 @@ public:
|
||||||
~ClientModel();
|
~ClientModel();
|
||||||
|
|
||||||
OptionsModel *getOptionsModel();
|
OptionsModel *getOptionsModel();
|
||||||
|
PeerTableModel *getPeerTableModel();
|
||||||
|
|
||||||
//! Return number of connections, default is in- and outbound (total)
|
//! Return number of connections, default is in- and outbound (total)
|
||||||
int getNumConnections(unsigned int flags = CONNECTIONS_ALL) const;
|
int getNumConnections(unsigned int flags = CONNECTIONS_ALL) const;
|
||||||
|
@ -71,6 +73,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
OptionsModel *optionsModel;
|
OptionsModel *optionsModel;
|
||||||
|
PeerTableModel *peerTableModel;
|
||||||
|
|
||||||
int cachedNumBlocks;
|
int cachedNumBlocks;
|
||||||
bool cachedReindexing;
|
bool cachedReindexing;
|
||||||
|
|
|
@ -652,6 +652,281 @@
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
<widget class="QWidget" name="tab_peers">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>&Peers</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QGridLayout" name="gridLayout_2">
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QLabel" name="peerHeading">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Select a peer to view detailed information.</string>
|
||||||
|
</property>
|
||||||
|
<property name="margin">
|
||||||
|
<number>3</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0" rowspan="2">
|
||||||
|
<widget class="QTableView" name="peerWidget">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="horizontalScrollBarPolicy">
|
||||||
|
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||||
|
</property>
|
||||||
|
<property name="editTriggers">
|
||||||
|
<set>QAbstractItemView::AnyKeyPressed|QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed</set>
|
||||||
|
</property>
|
||||||
|
<property name="sortingEnabled">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QWidget" name="detailWidget" native="true">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout_3">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>3</number>
|
||||||
|
</property>
|
||||||
|
<item row="12" column="0">
|
||||||
|
<widget class="QLabel" name="label_21">
|
||||||
|
<property name="text">
|
||||||
|
<string>Version:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="11" column="1">
|
||||||
|
<widget class="QLabel" name="peerPingTime">
|
||||||
|
<property name="text">
|
||||||
|
<string>N/A</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="0">
|
||||||
|
<widget class="QLabel" name="label_19">
|
||||||
|
<property name="text">
|
||||||
|
<string>Last Receive:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="14" column="0">
|
||||||
|
<widget class="QLabel" name="label_28">
|
||||||
|
<property name="text">
|
||||||
|
<string>User Agent:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="12" column="1">
|
||||||
|
<widget class="QLabel" name="peerVersion">
|
||||||
|
<property name="text">
|
||||||
|
<string>N/A</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="8" column="1">
|
||||||
|
<widget class="QLabel" name="peerConnTime">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>160</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>N/A</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="11" column="0">
|
||||||
|
<widget class="QLabel" name="label_26">
|
||||||
|
<property name="text">
|
||||||
|
<string>Ping Time:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="1">
|
||||||
|
<widget class="QLabel" name="peerLastRecv">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>N/A</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="8" column="0">
|
||||||
|
<widget class="QLabel" name="label_22">
|
||||||
|
<property name="text">
|
||||||
|
<string>Connection Time:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="1">
|
||||||
|
<widget class="QLabel" name="peerBytesSent">
|
||||||
|
<property name="text">
|
||||||
|
<string>N/A</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="14" column="1">
|
||||||
|
<widget class="QLabel" name="peerSubversion">
|
||||||
|
<property name="text">
|
||||||
|
<string>N/A</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="15" column="0">
|
||||||
|
<widget class="QLabel" name="label_29">
|
||||||
|
<property name="text">
|
||||||
|
<string>Starting Height:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="7" column="1">
|
||||||
|
<widget class="QLabel" name="peerBytesRecv">
|
||||||
|
<property name="text">
|
||||||
|
<string>N/A</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="0">
|
||||||
|
<widget class="QLabel" name="label_18">
|
||||||
|
<property name="text">
|
||||||
|
<string>Bytes Sent:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="7" column="0">
|
||||||
|
<widget class="QLabel" name="label_20">
|
||||||
|
<property name="text">
|
||||||
|
<string>Bytes Received:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="15" column="1">
|
||||||
|
<widget class="QLabel" name="peerHeight">
|
||||||
|
<property name="text">
|
||||||
|
<string>N/A</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="16" column="0">
|
||||||
|
<widget class="QLabel" name="label_24">
|
||||||
|
<property name="text">
|
||||||
|
<string>Ban Score:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="16" column="1">
|
||||||
|
<widget class="QLabel" name="peerBanScore">
|
||||||
|
<property name="text">
|
||||||
|
<string>N/A</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="17" column="0">
|
||||||
|
<widget class="QLabel" name="label_23">
|
||||||
|
<property name="text">
|
||||||
|
<string>Direction:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="17" column="1">
|
||||||
|
<widget class="QLabel" name="peerDirection">
|
||||||
|
<property name="text">
|
||||||
|
<string>N/A</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="19" column="0">
|
||||||
|
<widget class="QLabel" name="label_25">
|
||||||
|
<property name="text">
|
||||||
|
<string>Sync Node:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="19" column="1">
|
||||||
|
<widget class="QLabel" name="peerSyncNode">
|
||||||
|
<property name="text">
|
||||||
|
<string>N/A</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="label_15">
|
||||||
|
<property name="text">
|
||||||
|
<string>Last Send:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="text">
|
||||||
|
<string>Services:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_27">
|
||||||
|
<property name="text">
|
||||||
|
<string>IP Address/port:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QLabel" name="peerLastSend">
|
||||||
|
<property name="text">
|
||||||
|
<string>N/A</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QLabel" name="peerServices">
|
||||||
|
<property name="text">
|
||||||
|
<string>N/A</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QLabel" name="peerAddr">
|
||||||
|
<property name="text">
|
||||||
|
<string>N/A</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="20" column="0">
|
||||||
|
<widget class="QWidget" name="widget" native="true">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
#include "init.h"
|
#include "init.h"
|
||||||
|
#include "protocol.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
|
@ -754,4 +755,51 @@ QString boostPathToQString(const boost::filesystem::path &path)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
QString formatDurationStr(int secs)
|
||||||
|
{
|
||||||
|
QStringList strList;
|
||||||
|
int days = secs / 86400;
|
||||||
|
int hours = (secs % 86400) / 3600;
|
||||||
|
int mins = (secs % 3600) / 60;
|
||||||
|
int seconds = secs % 60;
|
||||||
|
|
||||||
|
if (days)
|
||||||
|
strList.append(QString(QObject::tr("%1 d")).arg(days));
|
||||||
|
if (hours)
|
||||||
|
strList.append(QString(QObject::tr("%1 h")).arg(hours));
|
||||||
|
if (mins)
|
||||||
|
strList.append(QString(QObject::tr("%1 m")).arg(mins));
|
||||||
|
if (seconds || (!days && !hours && !mins))
|
||||||
|
strList.append(QString(QObject::tr("%1 s")).arg(seconds));
|
||||||
|
|
||||||
|
return strList.join(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString formatServicesStr(uint64_t mask)
|
||||||
|
{
|
||||||
|
QStringList strList;
|
||||||
|
|
||||||
|
// Just scan the last 8 bits for now.
|
||||||
|
for (int i=0; i < 8; i++) {
|
||||||
|
uint64_t check = 1 << i;
|
||||||
|
if (mask & check)
|
||||||
|
{
|
||||||
|
switch (check)
|
||||||
|
{
|
||||||
|
case NODE_NETWORK:
|
||||||
|
strList.append(QObject::tr("NETWORK"));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
strList.append(QString("%1[%2]").arg(QObject::tr("UNKNOWN")).arg(check));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strList.size())
|
||||||
|
return strList.join(" & ");
|
||||||
|
else
|
||||||
|
return QObject::tr("None");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace GUIUtil
|
} // namespace GUIUtil
|
||||||
|
|
|
@ -173,6 +173,12 @@ namespace GUIUtil
|
||||||
/* Convert OS specific boost path to QString through UTF-8 */
|
/* Convert OS specific boost path to QString through UTF-8 */
|
||||||
QString boostPathToQString(const boost::filesystem::path &path);
|
QString boostPathToQString(const boost::filesystem::path &path);
|
||||||
|
|
||||||
|
/* Convert seconds into a QString with days, hours, mins, secs */
|
||||||
|
QString formatDurationStr(int secs);
|
||||||
|
|
||||||
|
/* Format CNodeStats.nServices bitmask into a user-readable string */
|
||||||
|
QString formatServicesStr(uint64_t mask);
|
||||||
|
|
||||||
} // namespace GUIUtil
|
} // namespace GUIUtil
|
||||||
|
|
||||||
#endif // GUIUTIL_H
|
#endif // GUIUTIL_H
|
||||||
|
|
238
src/qt/peertablemodel.cpp
Normal file
238
src/qt/peertablemodel.cpp
Normal file
|
@ -0,0 +1,238 @@
|
||||||
|
// Copyright (c) 2011-2013 The Bitcoin developers
|
||||||
|
// Distributed under the MIT/X11 software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#include "peertablemodel.h"
|
||||||
|
|
||||||
|
#include "clientmodel.h"
|
||||||
|
|
||||||
|
#include "net.h"
|
||||||
|
#include "sync.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QList>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
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::Address:
|
||||||
|
return pLeft->addrName.compare(pRight->addrName) < 0;
|
||||||
|
case PeerTableModel::Subversion:
|
||||||
|
return pLeft->cleanSubVer.compare(pRight->cleanSubVer) < 0;
|
||||||
|
case PeerTableModel::Height:
|
||||||
|
return pLeft->nStartingHeight < pRight->nStartingHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// private implementation
|
||||||
|
class PeerTablePriv
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/** Local cache of peer information */
|
||||||
|
QList<CNodeCombinedStats> cachedNodeStats;
|
||||||
|
/** Column to sort nodes by */
|
||||||
|
int sortColumn;
|
||||||
|
/** Order (ascending or descending) to sort nodes by */
|
||||||
|
Qt::SortOrder sortOrder;
|
||||||
|
/** Index of rows by node ID */
|
||||||
|
std::map<NodeId, int> mapNodeRows;
|
||||||
|
|
||||||
|
/** Pull a full list of peers from vNodes into our cache */
|
||||||
|
void refreshPeers() {
|
||||||
|
TRY_LOCK(cs_vNodes, lockNodes);
|
||||||
|
{
|
||||||
|
if (!lockNodes)
|
||||||
|
{
|
||||||
|
// skip the refresh if we can't immediately get the lock
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cachedNodeStats.clear();
|
||||||
|
#if QT_VERSION >= 0x040700
|
||||||
|
cachedNodeStats.reserve(vNodes.size());
|
||||||
|
#endif
|
||||||
|
BOOST_FOREACH(CNode* pnode, vNodes)
|
||||||
|
{
|
||||||
|
CNodeCombinedStats stats;
|
||||||
|
stats.statestats.nMisbehavior = -1;
|
||||||
|
pnode->copyStats(stats.nodestats);
|
||||||
|
cachedNodeStats.append(stats);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we can, retrieve the CNodeStateStats for each node.
|
||||||
|
TRY_LOCK(cs_main, lockMain);
|
||||||
|
{
|
||||||
|
if (lockMain)
|
||||||
|
{
|
||||||
|
BOOST_FOREACH(CNodeCombinedStats &stats, cachedNodeStats)
|
||||||
|
{
|
||||||
|
GetNodeStateStats(stats.nodestats.nodeid, stats.statestats);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (sortColumn >= 0)
|
||||||
|
// sort cacheNodeStats (use stable sort to prevent rows jumping around unneceesarily)
|
||||||
|
qStableSort(cachedNodeStats.begin(), cachedNodeStats.end(), NodeLessThan(sortColumn, sortOrder));
|
||||||
|
|
||||||
|
// build index map
|
||||||
|
mapNodeRows.clear();
|
||||||
|
int row = 0;
|
||||||
|
BOOST_FOREACH(CNodeCombinedStats &stats, cachedNodeStats)
|
||||||
|
{
|
||||||
|
mapNodeRows.insert(std::pair<NodeId, int>(stats.nodestats.nodeid, row++));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int size()
|
||||||
|
{
|
||||||
|
return cachedNodeStats.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
CNodeCombinedStats *index(int idx)
|
||||||
|
{
|
||||||
|
if(idx >= 0 && idx < cachedNodeStats.size()) {
|
||||||
|
return &cachedNodeStats[idx];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
PeerTableModel::PeerTableModel(ClientModel *parent) :
|
||||||
|
QAbstractTableModel(parent),clientModel(parent),timer(0)
|
||||||
|
{
|
||||||
|
columns << tr("Address") << tr("User Agent") << tr("Start Height");
|
||||||
|
priv = new PeerTablePriv();
|
||||||
|
// default to unsorted
|
||||||
|
priv->sortColumn = -1;
|
||||||
|
|
||||||
|
// set up timer for auto refresh
|
||||||
|
timer = new QTimer();
|
||||||
|
connect(timer, SIGNAL(timeout()), SLOT(refresh()));
|
||||||
|
|
||||||
|
// load initial data
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PeerTableModel::startAutoRefresh(int msecs)
|
||||||
|
{
|
||||||
|
timer->setInterval(1000);
|
||||||
|
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 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant PeerTableModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
if(!index.isValid())
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
CNodeCombinedStats *rec = static_cast<CNodeCombinedStats*>(index.internalPointer());
|
||||||
|
|
||||||
|
if(role == Qt::DisplayRole)
|
||||||
|
{
|
||||||
|
switch(index.column())
|
||||||
|
{
|
||||||
|
case Address:
|
||||||
|
return QVariant(rec->nodestats.addrName.c_str());
|
||||||
|
case Subversion:
|
||||||
|
return QVariant(rec->nodestats.cleanSubVer.c_str());
|
||||||
|
case Height:
|
||||||
|
return rec->nodestats.nStartingHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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 0;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return QModelIndex();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const CNodeCombinedStats *PeerTableModel::getNodeStats(int idx) {
|
||||||
|
return priv->index(idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PeerTableModel::refresh()
|
||||||
|
{
|
||||||
|
emit layoutAboutToBeChanged();
|
||||||
|
priv->refreshPeers();
|
||||||
|
emit layoutChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
int PeerTableModel::getRowByNodeId(NodeId nodeid)
|
||||||
|
{
|
||||||
|
std::map<NodeId, int>::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();
|
||||||
|
}
|
79
src/qt/peertablemodel.h
Normal file
79
src/qt/peertablemodel.h
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
// Copyright (c) 2011-2013 The Bitcoin developers
|
||||||
|
// Distributed under the MIT/X11 software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#ifndef PEERTABLEMODEL_H
|
||||||
|
#define PEERTABLEMODEL_H
|
||||||
|
|
||||||
|
#include "main.h"
|
||||||
|
#include "net.h"
|
||||||
|
|
||||||
|
#include <QAbstractTableModel>
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
|
class PeerTablePriv;
|
||||||
|
class ClientModel;
|
||||||
|
|
||||||
|
class QTimer;
|
||||||
|
|
||||||
|
struct CNodeCombinedStats {
|
||||||
|
CNodeStats nodestats;
|
||||||
|
CNodeStateStats statestats;
|
||||||
|
};
|
||||||
|
|
||||||
|
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(ClientModel *parent = 0);
|
||||||
|
const CNodeCombinedStats *getNodeStats(int idx);
|
||||||
|
int getRowByNodeId(NodeId nodeid);
|
||||||
|
void startAutoRefresh(int msecs);
|
||||||
|
void stopAutoRefresh();
|
||||||
|
|
||||||
|
enum ColumnIndex {
|
||||||
|
Address = 0,
|
||||||
|
Subversion = 1,
|
||||||
|
Height = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @name Methods overridden from QAbstractTableModel
|
||||||
|
@{*/
|
||||||
|
int rowCount(const QModelIndex &parent) const;
|
||||||
|
int columnCount(const QModelIndex &parent) const;
|
||||||
|
QVariant data(const QModelIndex &index, int role) const;
|
||||||
|
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
|
||||||
|
QModelIndex index(int row, int column, const QModelIndex &parent) const;
|
||||||
|
Qt::ItemFlags flags(const QModelIndex &index) const;
|
||||||
|
void sort(int column, Qt::SortOrder order);
|
||||||
|
/*@}*/
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void refresh();
|
||||||
|
|
||||||
|
private:
|
||||||
|
ClientModel *clientModel;
|
||||||
|
QStringList columns;
|
||||||
|
PeerTablePriv *priv;
|
||||||
|
QTimer *timer;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // PEERTABLEMODEL_H
|
|
@ -7,6 +7,10 @@
|
||||||
|
|
||||||
#include "clientmodel.h"
|
#include "clientmodel.h"
|
||||||
#include "guiutil.h"
|
#include "guiutil.h"
|
||||||
|
#include "peertablemodel.h"
|
||||||
|
|
||||||
|
#include "main.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
#include "rpcserver.h"
|
#include "rpcserver.h"
|
||||||
#include "rpcclient.h"
|
#include "rpcclient.h"
|
||||||
|
@ -195,6 +199,10 @@ RPCConsole::RPCConsole(QWidget *parent) :
|
||||||
clientModel(0),
|
clientModel(0),
|
||||||
historyPtr(0)
|
historyPtr(0)
|
||||||
{
|
{
|
||||||
|
detailNodeStats = CNodeCombinedStats();
|
||||||
|
detailNodeStats.nodestats.nodeid = -1;
|
||||||
|
detailNodeStats.statestats.nMisbehavior = -1;
|
||||||
|
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
GUIUtil::restoreWindowGeometry("nRPCConsoleWindow", this->size(), this);
|
GUIUtil::restoreWindowGeometry("nRPCConsoleWindow", this->size(), this);
|
||||||
|
|
||||||
|
@ -214,6 +222,7 @@ RPCConsole::RPCConsole(QWidget *parent) :
|
||||||
|
|
||||||
startExecutor();
|
startExecutor();
|
||||||
setTrafficGraphRange(INITIAL_TRAFFIC_GRAPH_MINS);
|
setTrafficGraphRange(INITIAL_TRAFFIC_GRAPH_MINS);
|
||||||
|
ui->detailWidget->hide();
|
||||||
|
|
||||||
clear();
|
clear();
|
||||||
}
|
}
|
||||||
|
@ -277,6 +286,23 @@ void RPCConsole::setClientModel(ClientModel *model)
|
||||||
updateTrafficStats(model->getTotalBytesRecv(), model->getTotalBytesSent());
|
updateTrafficStats(model->getTotalBytesRecv(), model->getTotalBytesSent());
|
||||||
connect(model, SIGNAL(bytesChanged(quint64,quint64)), this, SLOT(updateTrafficStats(quint64, quint64)));
|
connect(model, SIGNAL(bytesChanged(quint64,quint64)), this, SLOT(updateTrafficStats(quint64, quint64)));
|
||||||
|
|
||||||
|
// set up peer table
|
||||||
|
ui->peerWidget->setModel(model->getPeerTableModel());
|
||||||
|
ui->peerWidget->verticalHeader()->hide();
|
||||||
|
ui->peerWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||||
|
ui->peerWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||||
|
ui->peerWidget->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||||
|
ui->peerWidget->setColumnWidth(PeerTableModel::Address, ADDRESS_COLUMN_WIDTH);
|
||||||
|
columnResizingFixer = new GUIUtil::TableViewLastColumnResizingFixer(ui->peerWidget, MINIMUM_COLUMN_WIDTH, MINIMUM_COLUMN_WIDTH);
|
||||||
|
|
||||||
|
// connect the peerWidget's selection model to our peerSelected() handler
|
||||||
|
QItemSelectionModel *peerSelectModel = ui->peerWidget->selectionModel();
|
||||||
|
connect(peerSelectModel,
|
||||||
|
SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)),
|
||||||
|
this,
|
||||||
|
SLOT(peerSelected(const QItemSelection &, const QItemSelection &)));
|
||||||
|
connect(model->getPeerTableModel(), SIGNAL(layoutChanged()), this, SLOT(peerLayoutChanged()));
|
||||||
|
|
||||||
// Provide initial values
|
// Provide initial values
|
||||||
ui->clientVersion->setText(model->formatFullVersion());
|
ui->clientVersion->setText(model->formatFullVersion());
|
||||||
ui->clientName->setText(model->clientName());
|
ui->clientName->setText(model->clientName());
|
||||||
|
@ -474,17 +500,7 @@ QString RPCConsole::FormatBytes(quint64 bytes)
|
||||||
void RPCConsole::setTrafficGraphRange(int mins)
|
void RPCConsole::setTrafficGraphRange(int mins)
|
||||||
{
|
{
|
||||||
ui->trafficGraph->setGraphRangeMins(mins);
|
ui->trafficGraph->setGraphRangeMins(mins);
|
||||||
if(mins < 60) {
|
ui->lblGraphRange->setText(GUIUtil::formatDurationStr(mins * 60));
|
||||||
ui->lblGraphRange->setText(QString(tr("%1 m")).arg(mins));
|
|
||||||
} else {
|
|
||||||
int hours = mins / 60;
|
|
||||||
int minsLeft = mins % 60;
|
|
||||||
if(minsLeft == 0) {
|
|
||||||
ui->lblGraphRange->setText(QString(tr("%1 h")).arg(hours));
|
|
||||||
} else {
|
|
||||||
ui->lblGraphRange->setText(QString(tr("%1 h %2 m")).arg(hours).arg(minsLeft));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RPCConsole::updateTrafficStats(quint64 totalBytesIn, quint64 totalBytesOut)
|
void RPCConsole::updateTrafficStats(quint64 totalBytesIn, quint64 totalBytesOut)
|
||||||
|
@ -492,3 +508,157 @@ void RPCConsole::updateTrafficStats(quint64 totalBytesIn, quint64 totalBytesOut)
|
||||||
ui->lblBytesIn->setText(FormatBytes(totalBytesIn));
|
ui->lblBytesIn->setText(FormatBytes(totalBytesIn));
|
||||||
ui->lblBytesOut->setText(FormatBytes(totalBytesOut));
|
ui->lblBytesOut->setText(FormatBytes(totalBytesOut));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RPCConsole::peerSelected(const QItemSelection &selected, const QItemSelection &deselected)
|
||||||
|
{
|
||||||
|
if (selected.indexes().isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// mark the cached banscore as unknown
|
||||||
|
detailNodeStats.statestats.nMisbehavior = -1;
|
||||||
|
|
||||||
|
const CNodeCombinedStats *stats = clientModel->getPeerTableModel()->getNodeStats(selected.indexes().first().row());
|
||||||
|
|
||||||
|
if (stats)
|
||||||
|
{
|
||||||
|
detailNodeStats.nodestats.nodeid = stats->nodestats.nodeid;
|
||||||
|
updateNodeDetail(stats);
|
||||||
|
ui->detailWidget->show();
|
||||||
|
ui->detailWidget->setDisabled(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RPCConsole::peerLayoutChanged()
|
||||||
|
{
|
||||||
|
const CNodeCombinedStats *stats = NULL;
|
||||||
|
bool fUnselect = false, fReselect = false, fDisconnected = false;
|
||||||
|
|
||||||
|
if (detailNodeStats.nodestats.nodeid == -1)
|
||||||
|
// no node selected yet
|
||||||
|
return;
|
||||||
|
|
||||||
|
// find the currently selected row
|
||||||
|
int selectedRow;
|
||||||
|
QModelIndexList selectedModelIndex = ui->peerWidget->selectionModel()->selectedIndexes();
|
||||||
|
if (selectedModelIndex.isEmpty())
|
||||||
|
selectedRow = -1;
|
||||||
|
else
|
||||||
|
selectedRow = selectedModelIndex.first().row();
|
||||||
|
|
||||||
|
// check if our detail node has a row in the table (it may not necessarily
|
||||||
|
// be at selectedRow since its position can change after a layout change)
|
||||||
|
int detailNodeRow = clientModel->getPeerTableModel()->getRowByNodeId(detailNodeStats.nodestats.nodeid);
|
||||||
|
|
||||||
|
if (detailNodeRow < 0)
|
||||||
|
{
|
||||||
|
// detail node dissapeared from table (node disconnected)
|
||||||
|
fUnselect = true;
|
||||||
|
fDisconnected = true;
|
||||||
|
detailNodeStats.nodestats.nodeid = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (detailNodeRow != selectedRow)
|
||||||
|
{
|
||||||
|
// detail node moved position
|
||||||
|
fUnselect = true;
|
||||||
|
fReselect = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get fresh stats on the detail node.
|
||||||
|
stats = clientModel->getPeerTableModel()->getNodeStats(detailNodeRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fUnselect && selectedRow >= 0)
|
||||||
|
{
|
||||||
|
ui->peerWidget->selectionModel()->select(QItemSelection(selectedModelIndex.first(), selectedModelIndex.last()),
|
||||||
|
QItemSelectionModel::Deselect);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fReselect)
|
||||||
|
{
|
||||||
|
ui->peerWidget->selectRow(detailNodeRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stats)
|
||||||
|
updateNodeDetail(stats);
|
||||||
|
|
||||||
|
if (fDisconnected)
|
||||||
|
{
|
||||||
|
ui->peerHeading->setText(QString(tr("Peer Disconnected")));
|
||||||
|
ui->detailWidget->setDisabled(true);
|
||||||
|
QDateTime dt = QDateTime::fromTime_t(detailNodeStats.nodestats.nLastSend);
|
||||||
|
if (detailNodeStats.nodestats.nLastSend)
|
||||||
|
ui->peerLastSend->setText(dt.toString("yyyy-MM-dd hh:mm:ss"));
|
||||||
|
dt.setTime_t(detailNodeStats.nodestats.nLastRecv);
|
||||||
|
if (detailNodeStats.nodestats.nLastRecv)
|
||||||
|
ui->peerLastRecv->setText(dt.toString("yyyy-MM-dd hh:mm:ss"));
|
||||||
|
dt.setTime_t(detailNodeStats.nodestats.nTimeConnected);
|
||||||
|
ui->peerConnTime->setText(dt.toString("yyyy-MM-dd hh:mm:ss"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RPCConsole::updateNodeDetail(const CNodeCombinedStats *combinedStats)
|
||||||
|
{
|
||||||
|
CNodeStats stats = combinedStats->nodestats;
|
||||||
|
|
||||||
|
// keep a copy of timestamps, used to display dates upon disconnect
|
||||||
|
detailNodeStats.nodestats.nLastSend = stats.nLastSend;
|
||||||
|
detailNodeStats.nodestats.nLastRecv = stats.nLastRecv;
|
||||||
|
detailNodeStats.nodestats.nTimeConnected = stats.nTimeConnected;
|
||||||
|
|
||||||
|
// update the detail ui with latest node information
|
||||||
|
ui->peerHeading->setText(QString("<b>%1</b>").arg(tr("Node Detail")));
|
||||||
|
ui->peerAddr->setText(QString(stats.addrName.c_str()));
|
||||||
|
ui->peerServices->setText(GUIUtil::formatServicesStr(stats.nServices));
|
||||||
|
ui->peerLastSend->setText(stats.nLastSend ? GUIUtil::formatDurationStr(GetTime() - stats.nLastSend) : tr("never"));
|
||||||
|
ui->peerLastRecv->setText(stats.nLastRecv ? GUIUtil::formatDurationStr(GetTime() - stats.nLastRecv) : tr("never"));
|
||||||
|
ui->peerBytesSent->setText(FormatBytes(stats.nSendBytes));
|
||||||
|
ui->peerBytesRecv->setText(FormatBytes(stats.nRecvBytes));
|
||||||
|
ui->peerConnTime->setText(GUIUtil::formatDurationStr(GetTime() - stats.nTimeConnected));
|
||||||
|
ui->peerPingTime->setText(stats.dPingTime == 0 ? tr("N/A") : QString(tr("%1 secs")).arg(QString::number(stats.dPingTime, 'f', 3)));
|
||||||
|
ui->peerVersion->setText(QString("%1").arg(stats.nVersion));
|
||||||
|
ui->peerSubversion->setText(QString(stats.cleanSubVer.c_str()));
|
||||||
|
ui->peerDirection->setText(stats.fInbound ? tr("Inbound") : tr("Outbound"));
|
||||||
|
ui->peerHeight->setText(QString("%1").arg(stats.nStartingHeight));
|
||||||
|
ui->peerSyncNode->setText(stats.fSyncNode ? tr("Yes") : tr("No"));
|
||||||
|
|
||||||
|
// if we can, display the peer's ban score
|
||||||
|
CNodeStateStats statestats = combinedStats->statestats;
|
||||||
|
if (statestats.nMisbehavior >= 0)
|
||||||
|
{
|
||||||
|
// we have a new nMisbehavor value - update the cache
|
||||||
|
detailNodeStats.statestats.nMisbehavior = statestats.nMisbehavior;
|
||||||
|
}
|
||||||
|
|
||||||
|
// pull the ban score from cache. -1 means it hasn't been retrieved yet (lock busy).
|
||||||
|
if (detailNodeStats.statestats.nMisbehavior >= 0)
|
||||||
|
ui->peerBanScore->setText(QString("%1").arg(detailNodeStats.statestats.nMisbehavior));
|
||||||
|
else
|
||||||
|
ui->peerBanScore->setText(tr("Fetching..."));
|
||||||
|
}
|
||||||
|
|
||||||
|
void RPCConsole::resizeEvent(QResizeEvent *event)
|
||||||
|
{
|
||||||
|
QWidget::resizeEvent(event);
|
||||||
|
columnResizingFixer->stretchColumnWidth(PeerTableModel::Address);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RPCConsole::showEvent(QShowEvent *event)
|
||||||
|
{
|
||||||
|
QWidget::showEvent(event);
|
||||||
|
|
||||||
|
// peerWidget needs a resize in case the dialog has non-default geometry
|
||||||
|
columnResizingFixer->stretchColumnWidth(PeerTableModel::Address);
|
||||||
|
|
||||||
|
// start the PeerTableModel refresh timer
|
||||||
|
clientModel->getPeerTableModel()->startAutoRefresh(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RPCConsole::hideEvent(QHideEvent *event)
|
||||||
|
{
|
||||||
|
QWidget::hideEvent(event);
|
||||||
|
|
||||||
|
// stop PeerTableModel auto refresh
|
||||||
|
clientModel->getPeerTableModel()->stopAutoRefresh();
|
||||||
|
}
|
||||||
|
|
|
@ -5,10 +5,18 @@
|
||||||
#ifndef RPCCONSOLE_H
|
#ifndef RPCCONSOLE_H
|
||||||
#define RPCCONSOLE_H
|
#define RPCCONSOLE_H
|
||||||
|
|
||||||
|
#include "guiutil.h"
|
||||||
|
#include "net.h"
|
||||||
|
|
||||||
|
#include "peertablemodel.h"
|
||||||
|
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
|
|
||||||
class ClientModel;
|
class ClientModel;
|
||||||
|
|
||||||
|
class QItemSelection;
|
||||||
|
class CNodeCombinedStats;
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class RPCConsole;
|
class RPCConsole;
|
||||||
}
|
}
|
||||||
|
@ -35,6 +43,19 @@ public:
|
||||||
protected:
|
protected:
|
||||||
virtual bool eventFilter(QObject* obj, QEvent *event);
|
virtual bool eventFilter(QObject* obj, QEvent *event);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/** show detailed information on ui about selected node */
|
||||||
|
void updateNodeDetail(const CNodeCombinedStats *combinedStats);
|
||||||
|
|
||||||
|
enum ColumnWidths
|
||||||
|
{
|
||||||
|
ADDRESS_COLUMN_WIDTH = 250,
|
||||||
|
MINIMUM_COLUMN_WIDTH = 120
|
||||||
|
};
|
||||||
|
|
||||||
|
/** track the node that we are currently viewing detail on in the peers tab */
|
||||||
|
CNodeCombinedStats detailNodeStats;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void on_lineEdit_returnPressed();
|
void on_lineEdit_returnPressed();
|
||||||
void on_tabWidget_currentChanged(int index);
|
void on_tabWidget_currentChanged(int index);
|
||||||
|
@ -44,6 +65,9 @@ private slots:
|
||||||
void on_sldGraphRange_valueChanged(int value);
|
void on_sldGraphRange_valueChanged(int value);
|
||||||
/** update traffic statistics */
|
/** update traffic statistics */
|
||||||
void updateTrafficStats(quint64 totalBytesIn, quint64 totalBytesOut);
|
void updateTrafficStats(quint64 totalBytesIn, quint64 totalBytesOut);
|
||||||
|
void resizeEvent(QResizeEvent *event);
|
||||||
|
void showEvent(QShowEvent *event);
|
||||||
|
void hideEvent(QHideEvent *event);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void clear();
|
void clear();
|
||||||
|
@ -57,6 +81,10 @@ public slots:
|
||||||
void browseHistory(int offset);
|
void browseHistory(int offset);
|
||||||
/** Scroll console view to end */
|
/** Scroll console view to end */
|
||||||
void scrollToEnd();
|
void scrollToEnd();
|
||||||
|
/** Handle selection of peer in peers list */
|
||||||
|
void peerSelected(const QItemSelection &selected, const QItemSelection &deselected);
|
||||||
|
/** Handle updated peer information */
|
||||||
|
void peerLayoutChanged();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
// For RPC command executor
|
// For RPC command executor
|
||||||
|
@ -70,6 +98,7 @@ private:
|
||||||
Ui::RPCConsole *ui;
|
Ui::RPCConsole *ui;
|
||||||
ClientModel *clientModel;
|
ClientModel *clientModel;
|
||||||
QStringList history;
|
QStringList history;
|
||||||
|
GUIUtil::TableViewLastColumnResizingFixer *columnResizingFixer;
|
||||||
int historyPtr;
|
int historyPtr;
|
||||||
|
|
||||||
void startExecutor();
|
void startExecutor();
|
||||||
|
|
Loading…
Reference in a new issue