add speedtest querier and ui view

This commit is contained in:
Nova 2025-05-08 01:29:10 +03:30
parent bd1b1b1635
commit a2c5efc31d
7 changed files with 102 additions and 48 deletions

View File

@ -6,7 +6,6 @@ import (
"fmt"
"github.com/Mahdi-zarei/speedtest-go/speedtest"
"github.com/sagernet/sing-box/adapter"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/metadata"
"github.com/sagernet/sing/service"
"nekobox_core/internal/boxbox"
@ -206,14 +205,14 @@ func speedTestWithDialer(ctx context.Context, dialer func(ctx context.Context, n
defer func() { close(done) }()
if testDl {
err = srv[0].DownloadTestContext(ctx)
if err != nil {
if err != nil && !errors.Is(err, context.Canceled) {
res.Error = err
return
}
}
if testUl {
err = srv[0].UploadTestContext(ctx)
if err != nil {
if err != nil && !errors.Is(err, context.Canceled) {
res.Error = err
return
}
@ -232,7 +231,7 @@ func speedTestWithDialer(ctx context.Context, dialer func(ctx context.Context, n
SpTQuerier.storeResult(res)
return nil
case <-ctx.Done():
return E.New("test cancelled")
return nil
case <-ticker.C:
res.DlSpeed = speedtest.ByteRate(srv[0].Context.GetEWMADownloadRate()).String()
res.UlSpeed = speedtest.ByteRate(srv[0].Context.GetEWMAUploadRate()).String()

View File

@ -42,7 +42,7 @@ namespace NekoGui_rpc {
libcore::SpeedTestResponse SpeedTest(bool *rpcOK, const libcore::SpeedTestRequest &request);
libcore::SpeedTestResult QueryCurrentSpeedTests(bool *rpcOK);
libcore::QuerySpeedTestResponse QueryCurrentSpeedTests(bool *rpcOK);
private:
std::function<std::unique_ptr<QtGrpc::Http2GrpcChannelPrivate>()> make_grpc_channel;

View File

@ -1,6 +1,7 @@
#pragma once
#include <QMainWindow>
#include <core/server/gen/libcore.pb.h>
#include "include/global/NekoGui.hpp"
#include "include/stats/connections/connectionLister.hpp"
@ -86,6 +87,9 @@ public:
void UpdateConnectionListWithRecreate(const QList<NekoGui_traffic::ConnectionMetadata>& connections);
// TODO maybe use a more generalized way of passing data later, for now we only need it for speedtest data
void UpdateDataView(const libcore::SpeedTestResult& result, const QString& profileName, bool clear);
signals:
void profile_selected(int id);

View File

@ -165,23 +165,43 @@
</layout>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLineEdit" name="search">
<property name="clearButtonEnabled">
<widget class="QTextBrowser" name="data_view">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Ignored">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="mouseTracking">
<bool>false</bool>
</property>
<property name="focusPolicy">
<enum>Qt::FocusPolicy::NoFocus</enum>
</property>
<property name="contextMenuPolicy">
<enum>Qt::ContextMenuPolicy::NoContextMenu</enum>
</property>
<property name="acceptDrops">
<bool>false</bool>
</property>
<property name="frameShape">
<enum>QFrame::Shape::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Sunken</enum>
</property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarPolicy::ScrollBarAlwaysOff</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarPolicy::ScrollBarAlwaysOff</enum>
</property>
<property name="textInteractionFlags">
<set>Qt::TextInteractionFlag::LinksAccessibleByKeyboard|Qt::TextInteractionFlag::LinksAccessibleByMouse</set>
</property>
</widget>
</item>
</layout>
@ -536,7 +556,7 @@
<x>0</x>
<y>0</y>
<width>800</width>
<height>25</height>
<height>17</height>
</rect>
</property>
<widget class="QMenu" name="menu_program">

View File

@ -421,10 +421,10 @@ namespace NekoGui_rpc {
}
}
libcore::SpeedTestResult Client::QueryCurrentSpeedTests(bool *rpcOK)
libcore::QuerySpeedTestResponse Client::QueryCurrentSpeedTests(bool *rpcOK)
{
const libcore::EmptyReq req;
libcore::SpeedTestResult reply;
libcore::QuerySpeedTestResponse reply;
auto status = make_grpc_channel()->Call("QuerySpeedTest", req, &reply);
if (status == QNetworkReply::NoError) {

View File

@ -310,38 +310,13 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
ui->proxyListTable->setTabKeyNavigation(false);
// search box
ui->search->setVisible(false);
connect(shortcut_ctrl_f, &QShortcut::activated, this, [=] {
ui->search->setVisible(true);
ui->search->setFocus();
});
connect(shortcut_esc, &QShortcut::activated, this, [=] {
if (ui->search->isVisible()) {
ui->search->setText("");
ui->search->textChanged("");
ui->search->setVisible(false);
}
if (select_mode) {
emit profile_selected(-1);
select_mode = false;
refresh_status();
}
});
connect(ui->search, &QLineEdit::textChanged, this, [=](const QString &text) {
if (text.isEmpty()) {
for (int i = 0; i < ui->proxyListTable->rowCount(); i++) {
ui->proxyListTable->setRowHidden(i, false);
}
} else {
QList<QTableWidgetItem *> findItem = ui->proxyListTable->findItems(text, Qt::MatchContains);
for (int i = 0; i < ui->proxyListTable->rowCount(); i++) {
ui->proxyListTable->setRowHidden(i, true);
}
for (auto item: findItem) {
if (item != nullptr) ui->proxyListTable->setRowHidden(item->row(), false);
}
}
});
// refresh
this->refresh_groups();
@ -953,6 +928,27 @@ void MainWindow::neko_set_spmode_vpn(bool enable, bool save) {
if (NekoGui::dataStore->started_id >= 0) neko_start(NekoGui::dataStore->started_id);
}
void MainWindow::UpdateDataView(const libcore::SpeedTestResult& result, const QString& profileName, bool clear)
{
if (clear)
{
ui->data_view->clear();
return;
}
QString html = QString(
"<p style='text-align:center;margin:0;'>Running Speedtest: %1</p>"
"<p style='text-align:center; color:#3299FF;margin:0;'>Dl↓ %2</p>"
"<p style='text-align:center; color:#86C43F;margin:0;'>Ul↑ %3</p>"
"<p style='text-align:center;margin:0;'>Server: %4, %5</p>"
).arg(profileName,
result.dl_speed().c_str(),
result.ul_speed().c_str(),
result.server_country().c_str(),
result.server_name().c_str());
ui->data_view->setHtml(html);
}
void MainWindow::setupConnectionList()
{
ui->connections->horizontalHeader()->setHighlightSections(false);

View File

@ -223,8 +223,43 @@ void MainWindow::runSpeedTest(const QString& config, bool useDefault, const QStr
req.set_test_download(speedtestConf == NekoGui::TestConfig::FULL || speedtestConf == NekoGui::TestConfig::DL);
req.set_test_upload(speedtestConf == NekoGui::TestConfig::FULL || speedtestConf == NekoGui::TestConfig::UL);
// loop query result
auto doneMu = new QMutex;
doneMu->lock();
runOnNewThread([=]
{
bool ok;
while (true) {
QThread::msleep(100);
if (doneMu->tryLock())
{
break;
}
auto res = defaultClient->QueryCurrentSpeedTests(&ok);
if (!ok || !res.is_running())
{
continue;
}
auto profile = NekoGui::profileManager->GetProfile(tag2entID[res.result().outbound_tag().c_str()]);
if (profile == nullptr)
{
continue;
}
runOnUiThread([=]
{
UpdateDataView(res.result(), profile->bean->name, false);
});
}
runOnUiThread([=]
{
UpdateDataView({}, {}, true);
});
doneMu->unlock();
delete doneMu;
});
bool rpcOK;
auto result = defaultClient->SpeedTest(&rpcOK, req);
doneMu->unlock();
//
if (!rpcOK) return;