Browse Source

Merge pull request #4285

a5b2d9c [Qt] tweak new peers tab in console window (Philip Kaufmann)
0.10
Wladimir J. van der Laan 10 years ago
parent
commit
1913302fb7
No known key found for this signature in database
GPG Key ID: 74810B012346C9A6
  1. 364
      src/qt/forms/rpcconsole.ui
  2. 5
      src/qt/guiutil.cpp
  3. 3
      src/qt/guiutil.h
  4. 64
      src/qt/peertablemodel.cpp
  5. 9
      src/qt/peertablemodel.h
  6. 155
      src/qt/rpcconsole.cpp
  7. 29
      src/qt/rpcconsole.h

364
src/qt/forms/rpcconsole.ui

@ -428,7 +428,7 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="tab"> <widget class="QWidget" name="tab_nettraffic">
<attribute name="title"> <attribute name="title">
<string>&amp;Network Traffic</string> <string>&amp;Network Traffic</string>
</attribute> </attribute>
@ -683,6 +683,19 @@
<string>&amp;Peers</string> <string>&amp;Peers</string>
</attribute> </attribute>
<layout class="QGridLayout" name="gridLayout_2"> <layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0" rowspan="2">
<widget class="QTableView" name="peerWidget">
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<attribute name="horizontalHeaderHighlightSections">
<bool>false</bool>
</attribute>
</widget>
</item>
<item row="0" column="1"> <item row="0" column="1">
<widget class="QLabel" name="peerHeading"> <widget class="QLabel" name="peerHeading">
<property name="sizePolicy"> <property name="sizePolicy">
@ -691,262 +704,377 @@
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<property name="text"> <property name="minimumSize">
<string>Select a peer to view detailed information.</string> <size>
<width>300</width>
<height>32</height>
</size>
</property> </property>
<property name="margin"> <property name="font">
<number>3</number> <font>
<pointsize>10</pointsize>
</font>
</property> </property>
</widget> <property name="cursor">
</item> <cursorShape>IBeamCursor</cursorShape>
<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>
<property name="horizontalScrollBarPolicy"> <property name="text">
<enum>Qt::ScrollBarAlwaysOff</enum> <string>Select a peer to view detailed information.</string>
</property> </property>
<property name="editTriggers"> <property name="alignment">
<set>QAbstractItemView::AnyKeyPressed|QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed</set> <set>Qt::AlignHCenter|Qt::AlignTop</set>
</property> </property>
<property name="sortingEnabled"> <property name="wordWrap">
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget> </widget>
</item> </item>
<item row="1" column="1"> <item row="1" column="1">
<widget class="QWidget" name="detailWidget" native="true"> <widget class="QWidget" name="detailWidget" native="true">
<property name="sizePolicy"> <property name="minimumSize">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding"> <size>
<horstretch>0</horstretch> <width>300</width>
<verstretch>0</verstretch> <height>0</height>
</sizepolicy> </size>
</property> </property>
<layout class="QGridLayout" name="gridLayout_3"> <layout class="QGridLayout" name="gridLayout_3">
<property name="leftMargin"> <item row="0" column="0">
<number>3</number> <widget class="QLabel" name="label_23">
</property>
<item row="12" column="0">
<widget class="QLabel" name="label_21">
<property name="text"> <property name="text">
<string>Version:</string> <string>Direction</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="11" column="1"> <item row="0" column="2">
<widget class="QLabel" name="peerPingTime"> <widget class="QLabel" name="peerDirection">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
<property name="text"> <property name="text">
<string>N/A</string> <string>N/A</string>
</property> </property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget> </widget>
</item> </item>
<item row="5" column="0"> <item row="1" column="0">
<widget class="QLabel" name="label_19"> <widget class="QLabel" name="label_21">
<property name="text"> <property name="text">
<string>Last Receive:</string> <string>Version</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="14" column="0"> <item row="1" column="2">
<widget class="QLabel" name="label_28"> <widget class="QLabel" name="peerVersion">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
<property name="text"> <property name="text">
<string>User Agent:</string> <string>N/A</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property> </property>
</widget> </widget>
</item> </item>
<item row="12" column="1"> <item row="2" column="0">
<widget class="QLabel" name="peerVersion"> <widget class="QLabel" name="label_28">
<property name="text"> <property name="text">
<string>N/A</string> <string>User Agent</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="8" column="1"> <item row="2" column="2">
<widget class="QLabel" name="peerConnTime"> <widget class="QLabel" name="peerSubversion">
<property name="minimumSize"> <property name="cursor">
<size> <cursorShape>IBeamCursor</cursorShape>
<width>160</width>
<height>0</height>
</size>
</property> </property>
<property name="text"> <property name="text">
<string>N/A</string> <string>N/A</string>
</property> </property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget> </widget>
</item> </item>
<item row="11" column="0"> <item row="3" column="0">
<widget class="QLabel" name="label_26"> <widget class="QLabel" name="label_4">
<property name="text"> <property name="text">
<string>Ping Time:</string> <string>Services</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="1"> <item row="3" column="2">
<widget class="QLabel" name="peerLastRecv"> <widget class="QLabel" name="peerServices">
<property name="sizePolicy"> <property name="cursor">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <cursorShape>IBeamCursor</cursorShape>
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property> </property>
<property name="text"> <property name="text">
<string>N/A</string> <string>N/A</string>
</property> </property>
</widget> <property name="textFormat">
</item> <enum>Qt::PlainText</enum>
<item row="8" column="0"> </property>
<widget class="QLabel" name="label_22"> <property name="textInteractionFlags">
<property name="text"> <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
<string>Connection Time:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="6" column="1"> <item row="4" column="0">
<widget class="QLabel" name="peerBytesSent"> <widget class="QLabel" name="label_25">
<property name="text"> <property name="text">
<string>N/A</string> <string>Sync Node</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="14" column="1"> <item row="4" column="2">
<widget class="QLabel" name="peerSubversion"> <widget class="QLabel" name="peerSyncNode">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
<property name="text"> <property name="text">
<string>N/A</string> <string>N/A</string>
</property> </property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget> </widget>
</item> </item>
<item row="15" column="0"> <item row="5" column="0">
<widget class="QLabel" name="label_29"> <widget class="QLabel" name="label_29">
<property name="text"> <property name="text">
<string>Starting Height:</string> <string>Starting Height</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="7" column="1"> <item row="5" column="2">
<widget class="QLabel" name="peerBytesRecv"> <widget class="QLabel" name="peerHeight">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
<property name="text"> <property name="text">
<string>N/A</string> <string>N/A</string>
</property> </property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget> </widget>
</item> </item>
<item row="6" column="0"> <item row="6" column="0">
<widget class="QLabel" name="label_18"> <widget class="QLabel" name="label_27">
<property name="text"> <property name="text">
<string>Bytes Sent:</string> <string>Sync Height</string>
</property>
</widget>
</item>
<item row="6" column="2">
<widget class="QLabel" name="peerSyncHeight">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
<property name="text">
<string>N/A</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property> </property>
</widget> </widget>
</item> </item>
<item row="7" column="0"> <item row="7" column="0">
<widget class="QLabel" name="label_20"> <widget class="QLabel" name="label_24">
<property name="text"> <property name="text">
<string>Bytes Received:</string> <string>Ban Score</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="15" column="1"> <item row="7" column="2">
<widget class="QLabel" name="peerHeight"> <widget class="QLabel" name="peerBanScore">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
<property name="text"> <property name="text">
<string>N/A</string> <string>N/A</string>
</property> </property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget> </widget>
</item> </item>
<item row="16" column="0"> <item row="8" column="0">
<widget class="QLabel" name="label_24"> <widget class="QLabel" name="label_22">
<property name="text"> <property name="text">
<string>Ban Score:</string> <string>Connection Time</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="16" column="1"> <item row="8" column="2">
<widget class="QLabel" name="peerBanScore"> <widget class="QLabel" name="peerConnTime">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
<property name="text"> <property name="text">
<string>N/A</string> <string>N/A</string>
</property> </property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget> </widget>
</item> </item>
<item row="17" column="0"> <item row="9" column="0">
<widget class="QLabel" name="label_23"> <widget class="QLabel" name="label_15">
<property name="text"> <property name="text">
<string>Direction:</string> <string>Last Send</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="17" column="1"> <item row="9" column="2">
<widget class="QLabel" name="peerDirection"> <widget class="QLabel" name="peerLastSend">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
<property name="text"> <property name="text">
<string>N/A</string> <string>N/A</string>
</property> </property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget> </widget>
</item> </item>
<item row="19" column="0"> <item row="10" column="0">
<widget class="QLabel" name="label_25"> <widget class="QLabel" name="label_19">
<property name="text"> <property name="text">
<string>Sync Node:</string> <string>Last Receive</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="19" column="1"> <item row="10" column="2">
<widget class="QLabel" name="peerSyncNode"> <widget class="QLabel" name="peerLastRecv">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
<property name="text"> <property name="text">
<string>N/A</string> <string>N/A</string>
</property> </property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget> </widget>
</item> </item>
<item row="3" column="0"> <item row="11" column="0">
<widget class="QLabel" name="label_15"> <widget class="QLabel" name="label_18">
<property name="text"> <property name="text">
<string>Last Send:</string> <string>Bytes Sent</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="0"> <item row="11" column="2">
<widget class="QLabel" name="label_4"> <widget class="QLabel" name="peerBytesSent">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
<property name="text"> <property name="text">
<string>Services:</string> <string>N/A</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="0"> <item row="12" column="0">
<widget class="QLabel" name="label_27"> <widget class="QLabel" name="label_20">
<property name="text"> <property name="text">
<string>IP Address/port:</string> <string>Bytes Received</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="1"> <item row="12" column="2">
<widget class="QLabel" name="peerLastSend"> <widget class="QLabel" name="peerBytesRecv">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
<property name="text"> <property name="text">
<string>N/A</string> <string>N/A</string>
</property> </property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget> </widget>
</item> </item>
<item row="2" column="1"> <item row="13" column="0">
<widget class="QLabel" name="peerServices"> <widget class="QLabel" name="label_26">
<property name="text"> <property name="text">
<string>N/A</string> <string>Ping Time</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="1"> <item row="13" column="2">
<widget class="QLabel" name="peerAddr"> <widget class="QLabel" name="peerPingTime">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
<property name="text"> <property name="text">
<string>N/A</string> <string>N/A</string>
</property> </property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget> </widget>
</item> </item>
<item row="20" column="0"> <item row="14" column="1">
<widget class="QWidget" name="widget" native="true"> <spacer name="verticalSpacer_3">
<property name="sizePolicy"> <property name="orientation">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding"> <enum>Qt::Vertical</enum>
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property> </property>
</widget> <property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item> </item>
</layout> </layout>
</widget> </widget>

5
src/qt/guiutil.cpp

@ -804,4 +804,9 @@ QString formatServicesStr(uint64_t mask)
return QObject::tr("None"); return QObject::tr("None");
} }
QString formatPingTime(double dPingTime)
{
return dPingTime == 0 ? QObject::tr("N/A") : QString(QObject::tr("%1 s")).arg(QString::number(dPingTime, 'f', 3));
}
} // namespace GUIUtil } // namespace GUIUtil

3
src/qt/guiutil.h

@ -178,6 +178,9 @@ namespace GUIUtil
/* Format CNodeStats.nServices bitmask into a user-readable string */ /* Format CNodeStats.nServices bitmask into a user-readable string */
QString formatServicesStr(uint64_t mask); QString formatServicesStr(uint64_t mask);
/* Format a CNodeCombinedStats.dPingTime into a user-readable string or display N/A, if 0*/
QString formatPingTime(double dPingTime);
} // namespace GUIUtil } // namespace GUIUtil
#endif // GUIUTIL_H #endif // GUIUTIL_H

64
src/qt/peertablemodel.cpp

@ -5,6 +5,8 @@
#include "peertablemodel.h" #include "peertablemodel.h"
#include "clientmodel.h" #include "clientmodel.h"
#include "guiconstants.h"
#include "guiutil.h"
#include "net.h" #include "net.h"
#include "sync.h" #include "sync.h"
@ -15,8 +17,8 @@
bool NodeLessThan::operator()(const CNodeCombinedStats &left, const CNodeCombinedStats &right) const bool NodeLessThan::operator()(const CNodeCombinedStats &left, const CNodeCombinedStats &right) const
{ {
const CNodeStats *pLeft = &(left.nodestats); const CNodeStats *pLeft = &(left.nodeStats);
const CNodeStats *pRight = &(right.nodestats); const CNodeStats *pRight = &(right.nodeStats);
if (order == Qt::DescendingOrder) if (order == Qt::DescendingOrder)
std::swap(pLeft, pRight); std::swap(pLeft, pRight);
@ -27,8 +29,8 @@ bool NodeLessThan::operator()(const CNodeCombinedStats &left, const CNodeCombine
return pLeft->addrName.compare(pRight->addrName) < 0; return pLeft->addrName.compare(pRight->addrName) < 0;
case PeerTableModel::Subversion: case PeerTableModel::Subversion:
return pLeft->cleanSubVer.compare(pRight->cleanSubVer) < 0; return pLeft->cleanSubVer.compare(pRight->cleanSubVer) < 0;
case PeerTableModel::Height: case PeerTableModel::Ping:
return pLeft->nStartingHeight < pRight->nStartingHeight; return pLeft->dPingTime < pRight->dPingTime;
} }
return false; return false;
@ -48,7 +50,8 @@ public:
std::map<NodeId, int> mapNodeRows; std::map<NodeId, int> mapNodeRows;
/** Pull a full list of peers from vNodes into our cache */ /** Pull a full list of peers from vNodes into our cache */
void refreshPeers() { void refreshPeers()
{
{ {
TRY_LOCK(cs_vNodes, lockNodes); TRY_LOCK(cs_vNodes, lockNodes);
if (!lockNodes) if (!lockNodes)
@ -63,23 +66,17 @@ public:
BOOST_FOREACH(CNode* pnode, vNodes) BOOST_FOREACH(CNode* pnode, vNodes)
{ {
CNodeCombinedStats stats; CNodeCombinedStats stats;
stats.statestats.nMisbehavior = -1; stats.nodeStateStats.nMisbehavior = 0;
pnode->copyStats(stats.nodestats); stats.nodeStateStats.nSyncHeight = -1;
stats.fNodeStateStatsAvailable = false;
pnode->copyStats(stats.nodeStats);
cachedNodeStats.append(stats); cachedNodeStats.append(stats);
} }
} }
// if we can, retrieve the CNodeStateStats for each node. // Try to retrieve the CNodeStateStats for each node.
{ BOOST_FOREACH(CNodeCombinedStats &stats, cachedNodeStats)
TRY_LOCK(cs_main, lockMain); stats.fNodeStateStatsAvailable = GetNodeStateStats(stats.nodeStats.nodeid, stats.nodeStateStats);
if (lockMain)
{
BOOST_FOREACH(CNodeCombinedStats &stats, cachedNodeStats)
{
GetNodeStateStats(stats.nodestats.nodeid, stats.statestats);
}
}
}
if (sortColumn >= 0) if (sortColumn >= 0)
// sort cacheNodeStats (use stable sort to prevent rows jumping around unneceesarily) // sort cacheNodeStats (use stable sort to prevent rows jumping around unneceesarily)
@ -89,9 +86,7 @@ public:
mapNodeRows.clear(); mapNodeRows.clear();
int row = 0; int row = 0;
BOOST_FOREACH(CNodeCombinedStats &stats, cachedNodeStats) BOOST_FOREACH(CNodeCombinedStats &stats, cachedNodeStats)
{ mapNodeRows.insert(std::pair<NodeId, int>(stats.nodeStats.nodeid, row++));
mapNodeRows.insert(std::pair<NodeId, int>(stats.nodestats.nodeid, row++));
}
} }
int size() int size()
@ -103,18 +98,18 @@ public:
{ {
if(idx >= 0 && idx < cachedNodeStats.size()) { if(idx >= 0 && idx < cachedNodeStats.size()) {
return &cachedNodeStats[idx]; return &cachedNodeStats[idx];
} } else {
else
{
return 0; return 0;
} }
} }
}; };
PeerTableModel::PeerTableModel(ClientModel *parent) : PeerTableModel::PeerTableModel(ClientModel *parent) :
QAbstractTableModel(parent),clientModel(parent),timer(0) QAbstractTableModel(parent),
clientModel(parent),
timer(0)
{ {
columns << tr("Address") << tr("User Agent") << tr("Start Height"); columns << tr("Address/Hostname") << tr("User Agent") << tr("Ping Time");
priv = new PeerTablePriv(); priv = new PeerTablePriv();
// default to unsorted // default to unsorted
priv->sortColumn = -1; priv->sortColumn = -1;
@ -122,14 +117,14 @@ PeerTableModel::PeerTableModel(ClientModel *parent) :
// set up timer for auto refresh // set up timer for auto refresh
timer = new QTimer(); timer = new QTimer();
connect(timer, SIGNAL(timeout()), SLOT(refresh())); connect(timer, SIGNAL(timeout()), SLOT(refresh()));
timer->setInterval(MODEL_UPDATE_DELAY);
// load initial data // load initial data
refresh(); refresh();
} }
void PeerTableModel::startAutoRefresh(int msecs) void PeerTableModel::startAutoRefresh()
{ {
timer->setInterval(1000);
timer->start(); timer->start();
} }
@ -147,7 +142,7 @@ int PeerTableModel::rowCount(const QModelIndex &parent) const
int PeerTableModel::columnCount(const QModelIndex &parent) const int PeerTableModel::columnCount(const QModelIndex &parent) const
{ {
Q_UNUSED(parent); Q_UNUSED(parent);
return 3; return columns.length();;
} }
QVariant PeerTableModel::data(const QModelIndex &index, int role) const QVariant PeerTableModel::data(const QModelIndex &index, int role) const
@ -162,11 +157,11 @@ QVariant PeerTableModel::data(const QModelIndex &index, int role) const
switch(index.column()) switch(index.column())
{ {
case Address: case Address:
return QVariant(rec->nodestats.addrName.c_str()); return QString::fromStdString(rec->nodeStats.addrName);
case Subversion: case Subversion:
return QVariant(rec->nodestats.cleanSubVer.c_str()); return QString::fromStdString(rec->nodeStats.cleanSubVer);
case Height: case Ping:
return rec->nodestats.nStartingHeight; return GUIUtil::formatPingTime(rec->nodeStats.dPingTime);
} }
} }
return QVariant(); return QVariant();
@ -208,7 +203,8 @@ QModelIndex PeerTableModel::index(int row, int column, const QModelIndex &parent
} }
} }
const CNodeCombinedStats *PeerTableModel::getNodeStats(int idx) { const CNodeCombinedStats *PeerTableModel::getNodeStats(int idx)
{
return priv->index(idx); return priv->index(idx);
} }

9
src/qt/peertablemodel.h

@ -19,8 +19,9 @@ class QTimer;
QT_END_NAMESPACE QT_END_NAMESPACE
struct CNodeCombinedStats { struct CNodeCombinedStats {
CNodeStats nodestats; CNodeStats nodeStats;
CNodeStateStats statestats; CNodeStateStats nodeStateStats;
bool fNodeStateStatsAvailable;
}; };
class NodeLessThan class NodeLessThan
@ -47,13 +48,13 @@ public:
explicit PeerTableModel(ClientModel *parent = 0); explicit PeerTableModel(ClientModel *parent = 0);
const CNodeCombinedStats *getNodeStats(int idx); const CNodeCombinedStats *getNodeStats(int idx);
int getRowByNodeId(NodeId nodeid); int getRowByNodeId(NodeId nodeid);
void startAutoRefresh(int msecs); void startAutoRefresh();
void stopAutoRefresh(); void stopAutoRefresh();
enum ColumnIndex { enum ColumnIndex {
Address = 0, Address = 0,
Subversion = 1, Subversion = 1,
Height = 2 Ping = 2
}; };
/** @name Methods overridden from QAbstractTableModel /** @name Methods overridden from QAbstractTableModel

155
src/qt/rpcconsole.cpp

@ -10,6 +10,7 @@
#include "peertablemodel.h" #include "peertablemodel.h"
#include "main.h" #include "main.h"
#include "chainparams.h"
#include "rpcserver.h" #include "rpcserver.h"
#include "rpcclient.h" #include "rpcclient.h"
#include "util.h" #include "util.h"
@ -200,12 +201,9 @@ RPCConsole::RPCConsole(QWidget *parent) :
QDialog(parent), QDialog(parent),
ui(new Ui::RPCConsole), ui(new Ui::RPCConsole),
clientModel(0), clientModel(0),
historyPtr(0) historyPtr(0),
cachedNodeid(-1)
{ {
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);
@ -233,6 +231,7 @@ RPCConsole::RPCConsole(QWidget *parent) :
setTrafficGraphRange(INITIAL_TRAFFIC_GRAPH_MINS); setTrafficGraphRange(INITIAL_TRAFFIC_GRAPH_MINS);
ui->detailWidget->hide(); ui->detailWidget->hide();
ui->peerHeading->setText(tr("Select a peer to view detailed information."));
clear(); clear();
} }
@ -303,11 +302,11 @@ void RPCConsole::setClientModel(ClientModel *model)
ui->peerWidget->setSelectionBehavior(QAbstractItemView::SelectRows); ui->peerWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
ui->peerWidget->setSelectionMode(QAbstractItemView::SingleSelection); ui->peerWidget->setSelectionMode(QAbstractItemView::SingleSelection);
ui->peerWidget->setColumnWidth(PeerTableModel::Address, ADDRESS_COLUMN_WIDTH); ui->peerWidget->setColumnWidth(PeerTableModel::Address, ADDRESS_COLUMN_WIDTH);
columnResizingFixer = new GUIUtil::TableViewLastColumnResizingFixer(ui->peerWidget, MINIMUM_COLUMN_WIDTH, MINIMUM_COLUMN_WIDTH); ui->peerWidget->setColumnWidth(PeerTableModel::Subversion, SUBVERSION_COLUMN_WIDTH);
ui->peerWidget->setColumnWidth(PeerTableModel::Ping, PING_COLUMN_WIDTH);
// connect the peerWidget's selection model to our peerSelected() handler // connect the peerWidget selection model to our peerSelected() handler
QItemSelectionModel *peerSelectModel = ui->peerWidget->selectionModel(); connect(ui->peerWidget->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)),
connect(peerSelectModel, SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)),
this, SLOT(peerSelected(const QItemSelection &, const QItemSelection &))); this, SLOT(peerSelected(const QItemSelection &, const QItemSelection &)));
connect(model->getPeerTableModel(), SIGNAL(layoutChanged()), this, SLOT(peerLayoutChanged())); connect(model->getPeerTableModel(), SIGNAL(layoutChanged()), this, SLOT(peerLayoutChanged()));
@ -473,10 +472,6 @@ void RPCConsole::on_tabWidget_currentChanged(int index)
{ {
ui->lineEdit->setFocus(); ui->lineEdit->setFocus();
} }
else if(ui->tabWidget->widget(index) == ui->tab_peers)
{
initPeerTable();
}
} }
void RPCConsole::on_openDebugLogfileButton_clicked() void RPCConsole::on_openDebugLogfileButton_clicked()
@ -525,30 +520,24 @@ void RPCConsole::peerSelected(const QItemSelection &selected, const QItemSelecti
{ {
Q_UNUSED(deselected); Q_UNUSED(deselected);
if (selected.indexes().isEmpty()) if (!clientModel || selected.indexes().isEmpty())
return; return;
// mark the cached banscore as unknown
detailNodeStats.statestats.nMisbehavior = -1;
const CNodeCombinedStats *stats = clientModel->getPeerTableModel()->getNodeStats(selected.indexes().first().row()); const CNodeCombinedStats *stats = clientModel->getPeerTableModel()->getNodeStats(selected.indexes().first().row());
if (stats) if (stats)
{
detailNodeStats.nodestats.nodeid = stats->nodestats.nodeid;
updateNodeDetail(stats); updateNodeDetail(stats);
ui->detailWidget->show();
ui->detailWidget->setDisabled(false);
}
} }
void RPCConsole::peerLayoutChanged() void RPCConsole::peerLayoutChanged()
{ {
if (!clientModel)
return;
const CNodeCombinedStats *stats = NULL; const CNodeCombinedStats *stats = NULL;
bool fUnselect = false, fReselect = false, fDisconnected = false; bool fUnselect = false;
bool fReselect = false;
if (detailNodeStats.nodestats.nodeid == -1) if (cachedNodeid == -1) // no node selected yet
// no node selected yet
return; return;
// find the currently selected row // find the currently selected row
@ -561,14 +550,15 @@ void RPCConsole::peerLayoutChanged()
// check if our detail node has a row in the table (it may not necessarily // 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) // be at selectedRow since its position can change after a layout change)
int detailNodeRow = clientModel->getPeerTableModel()->getRowByNodeId(detailNodeStats.nodestats.nodeid); int detailNodeRow = clientModel->getPeerTableModel()->getRowByNodeId(cachedNodeid);
if (detailNodeRow < 0) if (detailNodeRow < 0)
{ {
// detail node dissapeared from table (node disconnected) // detail node dissapeared from table (node disconnected)
fUnselect = true; fUnselect = true;
fDisconnected = true; cachedNodeid = -1;
detailNodeStats.nodestats.nodeid = 0; ui->detailWidget->hide();
ui->peerHeading->setText(tr("Select a peer to view detailed information."));
} }
else else
{ {
@ -596,91 +586,64 @@ void RPCConsole::peerLayoutChanged()
if (stats) if (stats)
updateNodeDetail(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) void RPCConsole::updateNodeDetail(const CNodeCombinedStats *stats)
{ {
CNodeStats stats = combinedStats->nodestats; // Update cached nodeid
cachedNodeid = stats->nodeStats.nodeid;
// 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 // update the detail ui with latest node information
ui->peerHeading->setText(QString("<b>%1</b>").arg(tr("Node Detail"))); QString peerAddrDetails(QString::fromStdString(stats->nodeStats.addrName));
ui->peerAddr->setText(QString(stats.addrName.c_str())); if (!stats->nodeStats.addrLocal.empty())
ui->peerServices->setText(GUIUtil::formatServicesStr(stats.nServices)); peerAddrDetails += "<br />" + tr("via %1").arg(QString::fromStdString(stats->nodeStats.addrLocal));
ui->peerLastSend->setText(stats.nLastSend ? GUIUtil::formatDurationStr(GetTime() - stats.nLastSend) : tr("never")); ui->peerHeading->setText(peerAddrDetails);
ui->peerLastRecv->setText(stats.nLastRecv ? GUIUtil::formatDurationStr(GetTime() - stats.nLastRecv) : tr("never")); ui->peerServices->setText(GUIUtil::formatServicesStr(stats->nodeStats.nServices));
ui->peerBytesSent->setText(FormatBytes(stats.nSendBytes)); ui->peerLastSend->setText(stats->nodeStats.nLastSend ? GUIUtil::formatDurationStr(GetTime() - stats->nodeStats.nLastSend) : tr("never"));
ui->peerBytesRecv->setText(FormatBytes(stats.nRecvBytes)); ui->peerLastRecv->setText(stats->nodeStats.nLastRecv ? GUIUtil::formatDurationStr(GetTime() - stats->nodeStats.nLastRecv) : tr("never"));
ui->peerConnTime->setText(GUIUtil::formatDurationStr(GetTime() - stats.nTimeConnected)); ui->peerBytesSent->setText(FormatBytes(stats->nodeStats.nSendBytes));
ui->peerPingTime->setText(stats.dPingTime == 0 ? tr("N/A") : QString(tr("%1 secs")).arg(QString::number(stats.dPingTime, 'f', 3))); ui->peerBytesRecv->setText(FormatBytes(stats->nodeStats.nRecvBytes));
ui->peerVersion->setText(QString("%1").arg(stats.nVersion)); ui->peerConnTime->setText(GUIUtil::formatDurationStr(GetTime() - stats->nodeStats.nTimeConnected));
ui->peerSubversion->setText(QString(stats.cleanSubVer.c_str())); ui->peerPingTime->setText(GUIUtil::formatPingTime(stats->nodeStats.dPingTime));
ui->peerDirection->setText(stats.fInbound ? tr("Inbound") : tr("Outbound")); ui->peerVersion->setText(QString("%1").arg(stats->nodeStats.nVersion));
ui->peerHeight->setText(QString("%1").arg(stats.nStartingHeight)); ui->peerSubversion->setText(QString::fromStdString(stats->nodeStats.cleanSubVer));
ui->peerSyncNode->setText(stats.fSyncNode ? tr("Yes") : tr("No")); ui->peerDirection->setText(stats->nodeStats.fInbound ? tr("Inbound") : tr("Outbound"));
ui->peerHeight->setText(QString("%1").arg(stats->nodeStats.nStartingHeight));
// if we can, display the peer's ban score ui->peerSyncNode->setText(stats->nodeStats.fSyncNode ? tr("Yes") : tr("No"));
CNodeStateStats statestats = combinedStats->statestats;
if (statestats.nMisbehavior >= 0) // This check fails for example if the lock was busy and
{ // nodeStateStats couldn't be fetched.
// we have a new nMisbehavor value - update the cache if (stats->fNodeStateStatsAvailable) {
detailNodeStats.statestats.nMisbehavior = statestats.nMisbehavior; // Ban score is init to 0
} ui->peerBanScore->setText(QString("%1").arg(stats->nodeStateStats.nMisbehavior));
// pull the ban score from cache. -1 means it hasn't been retrieved yet (lock busy). // Sync height is init to -1
if (detailNodeStats.statestats.nMisbehavior >= 0) if (stats->nodeStateStats.nSyncHeight > -1)
ui->peerBanScore->setText(QString("%1").arg(detailNodeStats.statestats.nMisbehavior)); ui->peerSyncHeight->setText(QString("%1").arg(stats->nodeStateStats.nSyncHeight));
else else
ui->peerSyncHeight->setText(tr("Unknown"));
} else {
ui->peerBanScore->setText(tr("Fetching...")); ui->peerBanScore->setText(tr("Fetching..."));
} ui->peerSyncHeight->setText(tr("Fetching..."));
}
void RPCConsole::initPeerTable()
{
if (!clientModel)
return;
// peerWidget needs a resize in case the dialog has non-default geometry
columnResizingFixer->stretchColumnWidth(PeerTableModel::Address);
// start PeerTableModel auto refresh ui->detailWidget->show();
clientModel->getPeerTableModel()->startAutoRefresh(1000);
} }
// We override the virtual resizeEvent of the QWidget to adjust tables column
// sizes as the tables width is proportional to the dialogs width.
void RPCConsole::resizeEvent(QResizeEvent *event) void RPCConsole::resizeEvent(QResizeEvent *event)
{ {
QWidget::resizeEvent(event); QWidget::resizeEvent(event);
if (!clientModel)
return;
columnResizingFixer->stretchColumnWidth(PeerTableModel::Address);
} }
void RPCConsole::showEvent(QShowEvent *event) void RPCConsole::showEvent(QShowEvent *event)
{ {
QWidget::showEvent(event); QWidget::showEvent(event);
initPeerTable(); if (!clientModel)
return;
// start PeerTableModel auto refresh
clientModel->getPeerTableModel()->startAutoRefresh();
} }
void RPCConsole::hideEvent(QHideEvent *event) void RPCConsole::hideEvent(QHideEvent *event)

29
src/qt/rpcconsole.h

@ -44,21 +44,6 @@ 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);
/** initialize peer table */
void initPeerTable();
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);
@ -96,15 +81,23 @@ signals:
private: private:
static QString FormatBytes(quint64 bytes); static QString FormatBytes(quint64 bytes);
void startExecutor();
void setTrafficGraphRange(int mins); void setTrafficGraphRange(int mins);
/** show detailed information on ui about selected node */
void updateNodeDetail(const CNodeCombinedStats *stats);
enum ColumnWidths
{
ADDRESS_COLUMN_WIDTH = 200,
SUBVERSION_COLUMN_WIDTH = 100,
PING_COLUMN_WIDTH = 80
};
Ui::RPCConsole *ui; Ui::RPCConsole *ui;
ClientModel *clientModel; ClientModel *clientModel;
QStringList history; QStringList history;
GUIUtil::TableViewLastColumnResizingFixer *columnResizingFixer;
int historyPtr; int historyPtr;
NodeId cachedNodeid;
void startExecutor();
}; };
#endif // RPCCONSOLE_H #endif // RPCCONSOLE_H

Loading…
Cancel
Save