Skip to content

Commit 98fe51c

Browse files
authored
Merge pull request RetroShare#3087 from jolavillette/AddTableViewInRttStatistics
Add table view in rtt statistics
2 parents ea605d4 + 7500d8f commit 98fe51c

3 files changed

Lines changed: 234 additions & 47 deletions

File tree

retroshare-gui/src/gui/statistics/RttStatistics.cpp

Lines changed: 128 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,18 @@
1818
* *
1919
*******************************************************************************/
2020

21+
/*******************************************************************************
22+
* gui/statistics/RttStatistics.cpp *
23+
*******************************************************************************/
24+
2125
#include <iostream>
26+
#include <limits>
2227
#include <QTimer>
2328
#include <QObject>
24-
2529
#include <QPainter>
2630
#include <QStylePainter>
31+
#include <QHostAddress>
32+
#include <QHash>
2733

2834
#include <retroshare/rsrtt.h>
2935
#include <retroshare/rspeers.h>
@@ -32,28 +38,78 @@
3238

3339
#include "gui/settings/rsharesettings.h"
3440

41+
// --- Custom Item for Sorting (Table) ---
42+
class RttTreeItem : public QTreeWidgetItem {
43+
public:
44+
using QTreeWidgetItem::QTreeWidgetItem;
45+
46+
bool operator<(const QTreeWidgetItem &other) const {
47+
int sortCol = treeWidget() ? treeWidget()->sortColumn() : 0;
48+
49+
if (sortCol == RttStatistics::COL_RTT) {
50+
QString txt1 = text(RttStatistics::COL_RTT);
51+
QString txt2 = other.text(RttStatistics::COL_RTT);
52+
long val1 = (txt1 == "?") ? std::numeric_limits<long>::max() : txt1.toLong();
53+
long val2 = (txt2 == "?") ? std::numeric_limits<long>::max() : txt2.toLong();
54+
return val1 < val2;
55+
}
56+
57+
if (sortCol == RttStatistics::COL_IP_ADDRESS) {
58+
QString s1 = text(RttStatistics::COL_IP_ADDRESS);
59+
QString s2 = other.text(RttStatistics::COL_IP_ADDRESS);
60+
QHostAddress ip1(s1); QHostAddress ip2(s2);
61+
62+
bool isTorI2p1 = s1.contains(".onion") || s1.contains(".i2p");
63+
bool isTorI2p2 = s2.contains(".onion") || s2.contains(".i2p");
64+
65+
if (!ip1.isNull() && !ip2.isNull() && !isTorI2p1 && !isTorI2p2) {
66+
if (ip1.protocol() != ip2.protocol()) return ip1.protocol() < ip2.protocol();
67+
if (ip1.protocol() == QAbstractSocket::IPv4Protocol) return ip1.toIPv4Address() < ip2.toIPv4Address();
68+
}
69+
return s1 < s2;
70+
}
71+
return QTreeWidgetItem::operator<(other);
72+
}
73+
};
74+
75+
// --- Main Constructor ---
3576

3677
RttStatistics::RttStatistics(QWidget * /*parent*/)
3778
{
3879
setupUi(this) ;
39-
80+
4081
m_bProcessSettings = false;
4182

83+
// Setup Graph in Tab 1
4284
_tunnel_statistics_F->setWidget( _tst_CW = new RttStatisticsGraph(this) ) ;
4385
_tunnel_statistics_F->setWidgetResizable(true);
4486
_tunnel_statistics_F->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
4587
_tunnel_statistics_F->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
4688
_tunnel_statistics_F->viewport()->setBackgroundRole(QPalette::NoRole);
4789
_tunnel_statistics_F->setFrameStyle(QFrame::NoFrame);
4890
_tunnel_statistics_F->setFocusPolicy(Qt::NoFocus);
49-
91+
92+
// Setup Table in Tab 2
93+
treeWidget->setAlternatingRowColors(true);
94+
treeWidget->setSortingEnabled(true);
95+
treeWidget->sortByColumn(1, Qt::AscendingOrder);
96+
97+
// Timer for Table Update
98+
m_timer = new QTimer(this);
99+
connect(m_timer, SIGNAL(timeout()), this, SLOT(updateRttValues()));
100+
m_timer->start(1000);
101+
50102
// load settings
51103
processSettings(true);
52104
}
53105

54106
RttStatistics::~RttStatistics()
55107
{
56-
108+
if(m_timer) {
109+
m_timer->stop();
110+
delete m_timer; // Explicitly delete the timer as requested
111+
m_timer = nullptr;
112+
}
57113
// save settings
58114
processSettings(false);
59115
}
@@ -66,23 +122,82 @@ void RttStatistics::processSettings(bool bLoad)
66122

67123
if (bLoad) {
68124
// load settings
125+
QByteArray state = Settings->value("TreeState").toByteArray();
126+
if (!state.isEmpty()) treeWidget->header()->restoreState(state);
69127

70-
// state of splitter
71-
//splitter->restoreState(Settings->value("Splitter").toByteArray());
128+
tabWidget->setCurrentIndex(Settings->value("ActiveTab", 0).toInt());
72129
} else {
73130
// save settings
74-
75-
// state of splitter
76-
//Settings->setValue("Splitter", splitter->saveState());
77-
131+
Settings->setValue("TreeState", treeWidget->header()->saveState());
132+
Settings->setValue("ActiveTab", tabWidget->currentIndex());
78133
}
79134

80135
Settings->endGroup();
81-
136+
82137
m_bProcessSettings = false;
138+
}
139+
140+
// --- Table Update Logic (O(N) Optimized) ---
141+
void RttStatistics::updateRttValues()
142+
{
143+
// Only update if the RTT tab is visible and the table view is selected
144+
if (!isVisible() || tabWidget->currentIndex() != 1) return;
145+
146+
std::list<RsPeerId> idList;
147+
if (!rsPeers) return;
148+
rsPeers->getOnlineList(idList);
149+
150+
// 1. Collect all current items into a hash map to track them.
151+
// We use the Peer ID (stored in UserRole) as the key.
152+
QHash<QString, QTreeWidgetItem*> existingItems;
153+
for(int i = 0; i < treeWidget->topLevelItemCount(); ++i) {
154+
QTreeWidgetItem* item = treeWidget->topLevelItem(i);
155+
existingItems.insert(item->data(0, Qt::UserRole).toString(), item);
156+
}
157+
158+
for(std::list<RsPeerId>::const_iterator it = idList.begin(); it != idList.end(); ++it)
159+
{
160+
std::string peerIdStr = (*it).toStdString();
161+
QString qPeerId = QString::fromStdString(peerIdStr);
162+
163+
// Fetch RTT and peer details
164+
std::list<RsRttPongResult> results;
165+
int rttInMs = -1;
83166

167+
if (rsRtt->getPongResults(*it, 1, results) > 0 && !results.empty()) {
168+
float rttSec = results.back().mRTT;
169+
if (rttSec > 0.000001f) rttInMs = (int)(rttSec * 1000.0f);
170+
}
171+
172+
RsPeerDetails details;
173+
rsPeers->getPeerDetails(*it, details);
174+
QString peerName = QString::fromUtf8(details.name.c_str());
175+
QString ipAddress = QString::fromStdString(details.extAddr);
176+
177+
// 2. Take the item from the map.
178+
// This removes it from the 'existingItems' hash so it won't be deleted later.
179+
QTreeWidgetItem* item = existingItems.take(qPeerId);
180+
181+
if (!item) {
182+
// If the peer is new, create a new item
183+
item = new RttTreeItem(treeWidget);
184+
item->setData(0, Qt::UserRole, qPeerId);
185+
}
186+
187+
// Update the row values using the enums defined in the header
188+
item->setText(COL_PEER_NAME, peerName);
189+
item->setText(COL_RTT, (rttInMs != -1) ? QString::number(rttInMs) : "?");
190+
item->setText(COL_NODE_ID, qPeerId);
191+
item->setText(COL_IP_ADDRESS, ipAddress);
192+
}
193+
194+
// 3. Delete any items remaining in the hash map.
195+
// These correspond to peers that are no longer online or have been removed.
196+
qDeleteAll(existingItems);
84197
}
85198

199+
// --- Graph Source Implementation (Unchanged logic) ---
200+
86201
QString RttGraphSource::unitName() const
87202
{
88203
return QObject::tr("secs") ;
@@ -104,9 +219,10 @@ void RttGraphSource::getValues(std::map<std::string,float>& vals) const
104219
rsPeers->getOnlineList(idList);
105220

106221
vals.clear() ;
222+
std::list<RsRttPongResult> results ;
223+
107224
for(std::list<RsPeerId>::const_iterator it(idList.begin());it!=idList.end();++it)
108225
{
109-
std::list<RsRttPongResult> results ;
110226
if (rsRtt->getPongResults(*it, 1, results) > 0 && !results.empty()) {
111227
vals[(*it).toStdString()] = results.back().mRTT ;
112228
}

retroshare-gui/src/gui/statistics/RttStatistics.h

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,16 @@
1818
* *
1919
*******************************************************************************/
2020

21+
/*******************************************************************************
22+
* gui/statistics/RttStatistics.h *
23+
*******************************************************************************/
24+
2125
#pragma once
2226

2327
#include <QPoint>
28+
#include <QTimer>
29+
#include <QTreeWidget>
30+
#include <QHeaderView>
2431
#include <retroshare/rsrtt.h>
2532
#include <retroshare-gui/RsAutoUpdatePage.h>
2633

@@ -47,17 +54,28 @@ class RttStatisticsGraph: public RSGraphWidget
4754

4855
class RttStatistics: public MainPage, public Ui::RttStatistics
4956
{
57+
Q_OBJECT
58+
5059
public:
60+
61+
// Define column indices to avoid magic numbers
62+
enum TableColumns {
63+
COL_PEER_NAME = 0,
64+
COL_RTT = 1,
65+
COL_NODE_ID = 2,
66+
COL_IP_ADDRESS = 3
67+
};
68+
5169
RttStatistics(QWidget *parent = NULL) ;
5270
~RttStatistics();
5371

72+
private slots:
73+
void updateRttValues(); // Slot for table update
74+
5475
private:
5576
void processSettings(bool bLoad);
5677
bool m_bProcessSettings;
5778

5879
RttStatisticsGraph *_tst_CW ;
80+
QTimer *m_timer; // Timer for the table
5981
} ;
60-
61-
62-
63-

retroshare-gui/src/gui/statistics/RttStatistics.ui

Lines changed: 84 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,44 +6,97 @@
66
<rect>
77
<x>0</x>
88
<y>0</y>
9-
<width>611</width>
10-
<height>408</height>
9+
<width>700</width>
10+
<height>500</height>
1111
</rect>
1212
</property>
1313
<property name="windowTitle">
1414
<string>RTT Statistics</string>
1515
</property>
16-
<property name="windowIcon">
17-
<iconset resource="images.qrc">
18-
<normaloff>:/images/logo/logo_16.png</normaloff>:/images/logo/logo_16.png</iconset>
19-
</property>
20-
<layout class="QGridLayout" name="gridLayout">
21-
<item row="0" column="0">
22-
<widget class="QSplitter" name="splitter">
23-
<property name="orientation">
24-
<enum>Qt::Vertical</enum>
16+
<layout class="QVBoxLayout" name="verticalLayout_Main">
17+
<item>
18+
<widget class="QTabWidget" name="tabWidget">
19+
<property name="currentIndex">
20+
<number>0</number>
2521
</property>
26-
<widget class="QScrollArea" name="_tunnel_statistics_F">
27-
<property name="frameShape">
28-
<enum>QFrame::NoFrame</enum>
29-
</property>
30-
<property name="horizontalScrollBarPolicy">
31-
<enum>Qt::ScrollBarAlwaysOff</enum>
32-
</property>
33-
<property name="widgetResizable">
34-
<bool>true</bool>
35-
</property>
36-
<widget class="QWidget" name="scrollAreaWidgetContents">
37-
<property name="geometry">
38-
<rect>
39-
<x>0</x>
40-
<y>0</y>
41-
<width>593</width>
42-
<height>390</height>
43-
</rect>
44-
</property>
45-
</widget>
22+
23+
<widget class="QWidget" name="tab_graph">
24+
<attribute name="title">
25+
<string>Graph View</string>
26+
</attribute>
27+
<layout class="QGridLayout" name="gridLayout_Graph">
28+
<item row="0" column="0">
29+
<widget class="QSplitter" name="splitter">
30+
<property name="orientation">
31+
<enum>Qt::Vertical</enum>
32+
</property>
33+
<widget class="QScrollArea" name="_tunnel_statistics_F">
34+
<property name="frameShape">
35+
<enum>QFrame::NoFrame</enum>
36+
</property>
37+
<property name="horizontalScrollBarPolicy">
38+
<enum>Qt::ScrollBarAlwaysOff</enum>
39+
</property>
40+
<property name="widgetResizable">
41+
<bool>true</bool>
42+
</property>
43+
<widget class="QWidget" name="scrollAreaWidgetContents">
44+
<property name="geometry">
45+
<rect>
46+
<x>0</x>
47+
<y>0</y>
48+
<width>100</width>
49+
<height>100</height>
50+
</rect>
51+
</property>
52+
</widget>
53+
</widget>
54+
</widget>
55+
</item>
56+
</layout>
57+
</widget>
58+
59+
<widget class="QWidget" name="tab_table">
60+
<attribute name="title">
61+
<string>Table View</string>
62+
</attribute>
63+
<layout class="QVBoxLayout" name="verticalLayout_Table">
64+
<item>
65+
<widget class="QTreeWidget" name="treeWidget">
66+
<property name="rootIsDecorated">
67+
<bool>false</bool>
68+
</property>
69+
<property name="sortingEnabled">
70+
<bool>true</bool>
71+
</property>
72+
<property name="alternatingRowColors">
73+
<bool>true</bool>
74+
</property>
75+
<column>
76+
<property name="text">
77+
<string>Peer Name</string>
78+
</property>
79+
</column>
80+
<column>
81+
<property name="text">
82+
<string>RTT (ms)</string>
83+
</property>
84+
</column>
85+
<column>
86+
<property name="text">
87+
<string>Node ID</string>
88+
</property>
89+
</column>
90+
<column>
91+
<property name="text">
92+
<string>IP Address</string>
93+
</property>
94+
</column>
95+
</widget>
96+
</item>
97+
</layout>
4698
</widget>
99+
47100
</widget>
48101
</item>
49102
</layout>

0 commit comments

Comments
 (0)