[Qt] tweak new peers tab in console window

- remove starting height as table header and replace with ping time
- remove columnResizingFixer
- add local address (if available) in detailed node view (on top of the
  right view below the remote address)
- remove some .c_str() by using QString::fromStdString()
- rename Address to Address/Hostname
- rename secs to just s for ping time
- use MODEL_UPDATE_DELAY from guiconstants.h for the peer refresh time
- make PeerTableModel::columnCount() return no hard-coded value
- remove and cleanup dup private: section in RPCConsole header
- add new defaults for column sizes
- remove behaviour which keeps disconnected peers selected and also remove
  code which keeps track of last selected peer stats
- add sync height to detail view
- add some additional NULL pointer checks for clientModel in
  rpcconsole.cpp
This commit is contained in:
Philip Kaufmann 2014-06-04 12:06:18 +02:00
parent d97a58f883
commit a5b2d9c82e
7 changed files with 445 additions and 356 deletions

View File

@ -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="minimumSize">
<size>
<width>300</width>
<height>32</height>
</size>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
<property name="text"> <property name="text">
<string>Select a peer to view detailed information.</string> <string>Select a peer to view detailed information.</string>
</property> </property>
<property name="margin"> <property name="alignment">
<number>3</number> <set>Qt::AlignHCenter|Qt::AlignTop</set>
</property> </property>
</widget> <property name="wordWrap">
</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> <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>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_21">
<property name="text">
<string>Version</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="peerVersion">
<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>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_28">
<property name="text">
<string>User Agent</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLabel" name="peerSubversion">
<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>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Services</string>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QLabel" name="peerServices">
<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>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_25">
<property name="text">
<string>Sync Node</string>
</property>
</widget>
</item>
<item row="4" column="2">
<widget class="QLabel" name="peerSyncNode">
<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>
</widget> </widget>
</item> </item>
<item row="5" column="0"> <item row="5" column="0">
<widget class="QLabel" name="label_19"> <widget class="QLabel" name="label_29">
<property name="text"> <property name="text">
<string>Last Receive:</string> <string>Starting Height</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="14" column="0"> <item row="5" column="2">
<widget class="QLabel" name="label_28"> <widget class="QLabel" name="peerHeight">
<property name="text"> <property name="cursor">
<string>User Agent:</string> <cursorShape>IBeamCursor</cursorShape>
</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>
<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="11" column="0"> </property>
<widget class="QLabel" name="label_26"> <property name="textInteractionFlags">
<property name="text"> <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
<string>Ping Time:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="1"> <item row="6" column="0">
<widget class="QLabel" name="peerLastRecv"> <widget class="QLabel" name="label_27">
<property name="sizePolicy"> <property name="text">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <string>Sync Height</string>
<horstretch>0</horstretch> </property>
<verstretch>0</verstretch> </widget>
</sizepolicy> </item>
<item row="6" column="2">
<widget class="QLabel" name="peerSyncHeight">
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</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>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label_24">
<property name="text">
<string>Ban Score</string>
</property>
</widget>
</item>
<item row="7" column="2">
<widget class="QLabel" name="peerBanScore">
<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>
</widget> </widget>
</item> </item>
<item row="8" column="0"> <item row="8" column="0">
<widget class="QLabel" name="label_22"> <widget class="QLabel" name="label_22">
<property name="text"> <property name="text">
<string>Connection Time:</string> <string>Connection Time</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="6" column="1"> <item row="8" column="2">
<widget class="QLabel" name="peerBytesSent"> <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>
</item>
<item row="9" column="0">
<widget class="QLabel" name="label_15">
<property name="text">
<string>Last Send</string>
</property>
</widget>
</item>
<item row="9" column="2">
<widget class="QLabel" name="peerLastSend">
<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>
</widget>
</item>
<item row="10" column="0">
<widget class="QLabel" name="label_19">
<property name="text">
<string>Last Receive</string>
</property>
</widget>
</item>
<item row="10" column="2">
<widget class="QLabel" name="peerLastRecv">
<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>
</widget>
</item>
<item row="11" column="0">
<widget class="QLabel" name="label_18">
<property name="text">
<string>Bytes Sent</string>
</property>
</widget>
</item>
<item row="11" column="2">
<widget class="QLabel" name="peerBytesSent">
<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>
</widget>
</item>
<item row="12" column="0">
<widget class="QLabel" name="label_20">
<property name="text">
<string>Bytes Received</string>
</property>
</widget>
</item>
<item row="12" column="2">
<widget class="QLabel" name="peerBytesRecv">
<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>
</widget>
</item>
<item row="13" column="0">
<widget class="QLabel" name="label_26">
<property name="text">
<string>Ping Time</string>
</property>
</widget>
</item>
<item row="13" column="2">
<widget class="QLabel" name="peerPingTime">
<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>
</widget> </widget>
</item> </item>
<item row="14" column="1"> <item row="14" column="1">
<widget class="QLabel" name="peerSubversion"> <spacer name="verticalSpacer_3">
<property name="text"> <property name="orientation">
<string>N/A</string> <enum>Qt::Vertical</enum>
</property> </property>
</widget> <property name="sizeHint" stdset="0">
</item> <size>
<item row="15" column="0"> <width>20</width>
<widget class="QLabel" name="label_29"> <height>40</height>
<property name="text"> </size>
<string>Starting Height:</string>
</property> </property>
</widget> </spacer>
</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> </item>
</layout> </layout>
</widget> </widget>

View File

@ -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

View File

@ -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

View File

@ -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.
{
TRY_LOCK(cs_main, lockMain);
if (lockMain)
{
BOOST_FOREACH(CNodeCombinedStats &stats, cachedNodeStats) BOOST_FOREACH(CNodeCombinedStats &stats, cachedNodeStats)
{ stats.fNodeStateStatsAvailable = GetNodeStateStats(stats.nodeStats.nodeid, stats.nodeStateStats);
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);
} }

View File

@ -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

View File

@ -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()
{ {
const CNodeCombinedStats *stats = NULL; if (!clientModel)
bool fUnselect = false, fReselect = false, fDisconnected = false; return;
if (detailNodeStats.nodestats.nodeid == -1) const CNodeCombinedStats *stats = NULL;
// no node selected yet bool fUnselect = false;
bool fReselect = false;
if (cachedNodeid == -1) // 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));
ui->peerSyncNode->setText(stats->nodeStats.fSyncNode ? tr("Yes") : tr("No"));
// if we can, display the peer's ban score // This check fails for example if the lock was busy and
CNodeStateStats statestats = combinedStats->statestats; // nodeStateStats couldn't be fetched.
if (statestats.nMisbehavior >= 0) if (stats->fNodeStateStatsAvailable) {
{ // Ban score is init to 0
// we have a new nMisbehavor value - update the cache ui->peerBanScore->setText(QString("%1").arg(stats->nodeStateStats.nMisbehavior));
detailNodeStats.statestats.nMisbehavior = statestats.nMisbehavior;
// Sync height is init to -1
if (stats->nodeStateStats.nSyncHeight > -1)
ui->peerSyncHeight->setText(QString("%1").arg(stats->nodeStateStats.nSyncHeight));
else
ui->peerSyncHeight->setText(tr("Unknown"));
} else {
ui->peerBanScore->setText(tr("Fetching..."));
ui->peerSyncHeight->setText(tr("Fetching..."));
} }
// pull the ban score from cache. -1 means it hasn't been retrieved yet (lock busy). ui->detailWidget->show();
if (detailNodeStats.statestats.nMisbehavior >= 0)
ui->peerBanScore->setText(QString("%1").arg(detailNodeStats.statestats.nMisbehavior));
else
ui->peerBanScore->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
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)

View File

@ -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