diff --git a/src/Icons/oxygen/office-chart-bar.png b/src/Icons/oxygen/office-chart-bar.png new file mode 100644 index 000000000..dd8ae4e42 Binary files /dev/null and b/src/Icons/oxygen/office-chart-bar.png differ diff --git a/src/icons.qrc b/src/icons.qrc index c0a6f6e2a..23f9b7867 100644 --- a/src/icons.qrc +++ b/src/icons.qrc @@ -294,6 +294,7 @@ Icons/oxygen/network-server.png Icons/oxygen/network-wired.png Icons/oxygen/object-locked.png + Icons/oxygen/office-chart-bar.png Icons/oxygen/preferences-desktop.png Icons/oxygen/preferences-other.png Icons/oxygen/preferences-system-network.png diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index d4e85a436..89bf0fc4b 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -123,6 +123,7 @@ MainWindow::MainWindow(QWidget *parent, const QStringList& torrentCmdLine) : QMa actionSet_global_download_limit->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/download.png"))); actionCreate_torrent->setIcon(IconProvider::instance()->getIcon("document-edit")); actionAbout->setIcon(IconProvider::instance()->getIcon("help-about")); + actionStatistics->setIcon(IconProvider::instance()->getIcon("office-chart-bar")); actionBugReport->setIcon(IconProvider::instance()->getIcon("tools-report-bug")); actionDecreasePriority->setIcon(IconProvider::instance()->getIcon("go-down")); actionDelete->setIcon(IconProvider::instance()->getIcon("list-remove")); @@ -361,6 +362,8 @@ MainWindow::~MainWindow() { delete m_executionLog; if (aboutDlg) delete aboutDlg; + if (statsDlg) + delete statsDlg; if (options) delete options; if (downloadFromURLDialog) @@ -740,6 +743,13 @@ void MainWindow::on_actionAbout_triggered() { } } +void MainWindow::on_actionStatistics_triggered() { + if (statsDlg) + statsDlg->setFocus(); + else + statsDlg = new StatsDialog(this); +} + void MainWindow::showEvent(QShowEvent *e) { qDebug("** Show Event **"); diff --git a/src/mainwindow.h b/src/mainwindow.h index 4c79ae762..e27ed6c9b 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -36,6 +36,7 @@ #include #include "ui_mainwindow.h" #include "qtorrenthandle.h" +#include "statsdialog.h" class QBtSession; class downloadFromURL; @@ -93,6 +94,7 @@ protected slots: void dragEnterEvent(QDragEnterEvent *event); void toggleVisibility(QSystemTrayIcon::ActivationReason e = QSystemTrayIcon::Trigger); void on_actionAbout_triggered(); + void on_actionStatistics_triggered(); void on_actionCreate_torrent_triggered(); void on_actionWebsite_triggered() const; void on_actionBugReport_triggered() const; @@ -165,6 +167,7 @@ private: QPointer options; QPointer console; QPointer aboutDlg; + QPointer statsDlg; QPointer createTorrentDlg; QPointer downloadFromURLDialog; QPointer systrayIcon; diff --git a/src/mainwindow.ui b/src/mainwindow.ui index dc1a75221..aa7eaaaa3 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -72,6 +72,7 @@ + @@ -375,6 +376,11 @@ Minimize + + + Statistics + + diff --git a/src/qtlibtorrent/qbtsession.cpp b/src/qtlibtorrent/qbtsession.cpp index 639bc9c73..55be6d9dd 100755 --- a/src/qtlibtorrent/qbtsession.cpp +++ b/src/qtlibtorrent/qbtsession.cpp @@ -2781,6 +2781,14 @@ qlonglong QBtSession::getETA(const QString &hash) const return m_speedMonitor->getETA(hash); } +quint64 QBtSession::getAlltimeDL() const { + return m_speedMonitor->getAlltimeDL(); +} + +quint64 QBtSession::getAlltimeUL() const { + return m_speedMonitor->getAlltimeUL(); +} + void QBtSession::handleIPFilterParsed(int ruleCount) { addConsoleMessage(tr("Successfully parsed the provided IP filter: %1 rules were applied.", "%1 is a number").arg(ruleCount)); diff --git a/src/qtlibtorrent/qbtsession.h b/src/qtlibtorrent/qbtsession.h index e2b1fd30c..749b836b3 100755 --- a/src/qtlibtorrent/qbtsession.h +++ b/src/qtlibtorrent/qbtsession.h @@ -107,6 +107,8 @@ public: inline bool isLSDEnabled() const { return LSDEnabled; } inline bool isPexEnabled() const { return PeXEnabled; } inline bool isQueueingEnabled() const { return queueingEnabled; } + quint64 getAlltimeDL() const; + quint64 getAlltimeUL() const; public slots: QTorrentHandle addTorrent(QString path, bool fromScanDir = false, QString from_url = QString(), bool resumed = false); diff --git a/src/qtlibtorrent/torrentspeedmonitor.cpp b/src/qtlibtorrent/torrentspeedmonitor.cpp index 5b9d55cab..549949253 100644 --- a/src/qtlibtorrent/torrentspeedmonitor.cpp +++ b/src/qtlibtorrent/torrentspeedmonitor.cpp @@ -35,6 +35,7 @@ #include "qbtsession.h" #include "misc.h" #include "torrentspeedmonitor.h" +#include "qinisettings.h" using namespace libtorrent; @@ -64,12 +65,14 @@ TorrentSpeedMonitor::TorrentSpeedMonitor(QBtSession* session) : { connect(m_session, SIGNAL(deletedTorrent(QString)), SLOT(removeSamples(QString))); connect(m_session, SIGNAL(pausedTorrent(QTorrentHandle)), SLOT(removeSamples(QTorrentHandle))); + loadStats(); } TorrentSpeedMonitor::~TorrentSpeedMonitor() { m_abort = true; m_abortCond.wakeOne(); wait(); + saveStats(); } void TorrentSpeedMonitor::run() @@ -77,6 +80,7 @@ void TorrentSpeedMonitor::run() do { m_mutex.lock(); getSamples(); + saveStats(); m_abortCond.wait(&m_mutex, 1000); m_mutex.unlock(); } while(!m_abort); @@ -153,6 +157,16 @@ qlonglong TorrentSpeedMonitor::getETA(const QString &hash) const return (h.total_wanted() - h.total_wanted_done()) / speed_average.download; } +quint64 TorrentSpeedMonitor::getAlltimeDL() const { + QMutexLocker l(&m_mutex); + return alltimeDL; +} + +quint64 TorrentSpeedMonitor::getAlltimeUL() const { + QMutexLocker l(&m_mutex); + return alltimeUL; +} + void TorrentSpeedMonitor::getSamples() { const std::vector torrents = m_session->getSession()->get_torrents(); @@ -163,8 +177,27 @@ void TorrentSpeedMonitor::getSamples() try { torrent_status st = it->status(0x0); if (!st.paused) { - m_samples[misc::toQString(it->info_hash())].addSample(st.download_payload_rate, st.upload_payload_rate); + int up = st.upload_payload_rate; + int down = st.download_payload_rate; + m_samples[misc::toQString(it->info_hash())].addSample(down, up); + alltimeDL += down; + alltimeUL += up; } } catch(invalid_handle&) {} } } + +void TorrentSpeedMonitor::saveStats() const { + QIniSettings s; + QVariantHash v; + v.insert("AlltimeDL", alltimeDL); + v.insert("AlltimeUL", alltimeUL); + s.setValue("Stats/AllStats", v); +} + +void TorrentSpeedMonitor::loadStats() { + QIniSettings s; + QVariantHash v(s.value("Stats/AllStats", QVariantHash()).toHash()); + alltimeDL = v["AlltimeDL"].toULongLong(); + alltimeUL = v["AlltimeUL"].toULongLong(); +} diff --git a/src/qtlibtorrent/torrentspeedmonitor.h b/src/qtlibtorrent/torrentspeedmonitor.h index 4a29ac4e4..3a310a719 100644 --- a/src/qtlibtorrent/torrentspeedmonitor.h +++ b/src/qtlibtorrent/torrentspeedmonitor.h @@ -49,26 +49,30 @@ public: explicit TorrentSpeedMonitor(QBtSession* session); ~TorrentSpeedMonitor(); qlonglong getETA(const QString &hash) const; + quint64 getAlltimeDL() const; + quint64 getAlltimeUL() const; protected: void run(); private: void getSamples(); + void saveStats() const; + void loadStats(); private slots: void removeSamples(const QString& hash); void removeSamples(const QTorrentHandle& h); -private: - static const int sampling_interval = 1000; // 1s - private: bool m_abort; QWaitCondition m_abortCond; QHash m_samples; mutable QMutex m_mutex; QBtSession *m_session; + // Will overflow at 15.9 EiB + quint64 alltimeUL; + quint64 alltimeDL; }; #endif // TORRENTSPEEDMONITOR_H diff --git a/src/src.pro b/src/src.pro index 38be01971..01f51f16d 100644 --- a/src/src.pro +++ b/src/src.pro @@ -149,7 +149,8 @@ nox { updownratiodlg.h \ loglistwidget.h \ addnewtorrentdialog.h \ - autoexpandabledialog.h + autoexpandabledialog.h \ + statsdialog.h SOURCES += mainwindow.cpp \ ico.cpp \ @@ -167,7 +168,8 @@ nox { updownratiodlg.cpp \ loglistwidget.cpp \ addnewtorrentdialog.cpp \ - autoexpandabledialog.cpp + autoexpandabledialog.cpp \ + statsdialog.cpp win32 { HEADERS += programupdater.h @@ -193,7 +195,8 @@ nox { torrentimportdlg.ui \ executionlog.ui \ addnewtorrentdialog.ui \ - autoexpandabledialog.ui + autoexpandabledialog.ui \ + statsdialog.ui } DESTDIR = . diff --git a/src/statsdialog.cpp b/src/statsdialog.cpp new file mode 100644 index 000000000..14459f6c4 --- /dev/null +++ b/src/statsdialog.cpp @@ -0,0 +1,109 @@ +/* + * Bittorrent Client using Qt4 and libtorrent. + * Copyright (C) 2013 Nick Tiskov + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + * + * Contact : daymansmail@gmail.com + */ + +#include "statsdialog.h" +#include "ui_statsdialog.h" + +#include "misc.h" + +StatsDialog::StatsDialog(QWidget *parent) : QDialog(parent), ui(new Ui::StatsDialog) { + ui->setupUi(this); + setAttribute(Qt::WA_DeleteOnClose); + connect(ui->buttonOK, SIGNAL(clicked()), SLOT(close())); + session = QBtSession::instance(); + updateUI(); + t = new QTimer(this); + t->setInterval(1500); + connect(t, SIGNAL(timeout()), SLOT(updateUI())); + t->start(); + show(); +} + +StatsDialog::~StatsDialog() { + t->stop(); + delete t; + delete ui; +} + +void StatsDialog::updateUI() { + libtorrent::session* s = session->getSession(); + libtorrent::cache_status cache = s->get_cache_status(); + libtorrent::session_status ss = s->status(); + + // Alltime DL/UL + quint64 atd = session->getAlltimeDL(); + quint64 atu = session->getAlltimeUL(); + ui->labelAlltimeDL->setText(misc::friendlyUnit(atd)); + ui->labelAlltimeUL->setText(misc::friendlyUnit(atu)); + // Total waste (this session) + ui->labelWaste->setText(misc::friendlyUnit(ss.total_redundant_bytes + ss.total_failed_bytes)); + // Global ratio + ui->labelGlobalRatio->setText( + ( atd > 0 && atu > 0 ) ? + QString::number( (qreal)atu / (qreal)atd, 'f', 2) : + "-" + ); + // Cache hits + ui->labelCacheHits->setText( + ( cache.blocks_read > 0 && cache.blocks_read_hit > 0 ) ? + QString("%L1\%").arg(100. * (qreal)cache.blocks_read_hit / (qreal)cache.blocks_read, 0, 'f', 2) : + "-" + ); + // Buffers size + ui->labelTotalBuf->setText(misc::friendlyUnit(cache.total_used_buffers * 16 * 1024)); + // Disk overload (100%) equivalent + // From lt manual: disk_write_queue and disk_read_queue are the number of peers currently waiting on a disk write or disk read + // to complete before it receives or sends any more data on the socket. It'a a metric of how disk bound you are. + + // num_peers is not reliable (adds up peers, which didn't even overcome tcp handshake) + const std::vector torrents = session->getTorrents(); + std::vector::const_iterator iBegin = torrents.begin(); + std::vector::const_iterator iEnd = torrents.begin(); + quint32 peers = 0; + for ( ; iBegin < iEnd ; ++iBegin) + peers += (*iBegin).status().num_peers; + ui->labelWriteStarve->setText( + ( ss.disk_write_queue > 0 && peers > 0 ) ? + QString("%L1\%").arg(100. * (qreal)ss.disk_write_queue / (qreal)peers, 0, 'f', 2) : + QString("0\%") + ); + ui->labelReadStarve->setText( + ( ss.disk_read_queue > 0 && peers > 0 ) ? + QString("%L1\%").arg(100. * (qreal)ss.disk_read_queue / (qreal)peers, 0, 'f', 2) : + QString("0\%") + ); + // Disk queues + ui->labelQueuedJobs->setText(QString::number(cache.job_queue_length)); + ui->labelJobsTime->setText(QString::number(cache.average_job_time)); + ui->labelQueuedBytes->setText(misc::friendlyUnit(cache.queued_bytes)); + + // Total connected peers + ui->labelPeers->setText(QString::number(ss.num_peers)); +} diff --git a/src/statsdialog.h b/src/statsdialog.h new file mode 100644 index 000000000..004fde022 --- /dev/null +++ b/src/statsdialog.h @@ -0,0 +1,58 @@ +/* + * Bittorrent Client using Qt4 and libtorrent. + * Copyright (C) 2013 Nick Tiskov + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + * + * Contact : daymansmail@gmail.com + */ + +#ifndef STATSDIALOG_H +#define STATSDIALOG_H + +#include +#include +#include "qbtsession.h" + +namespace Ui { + class StatsDialog; +} + +class StatsDialog : public QDialog { + Q_OBJECT + +public: + explicit StatsDialog(QWidget *parent = 0); + ~StatsDialog(); + +private slots: + void updateUI(); + +private: + Ui::StatsDialog *ui; + QBtSession* session; + QTimer* t; +}; + +#endif // STATSDIALOG_H diff --git a/src/statsdialog.ui b/src/statsdialog.ui new file mode 100644 index 000000000..7829b15dc --- /dev/null +++ b/src/statsdialog.ui @@ -0,0 +1,253 @@ + + + StatsDialog + + + + 0 + 0 + 273 + 395 + + + + Statistics + + + + + + User statistics + + + + + + Total peer connections: + + + + + + + Global ratio: + + + + + + + TextLabel + + + + + + + Alltime download: + + + + + + + TextLabel + + + + + + + TextLabel + + + + + + + Alltime upload: + + + + + + + TextLabel + + + + + + + Total waste (this session): + + + + + + + TextLabel + + + + + + + + + + Cache statistics + + + + + + Read cache Hits: + + + + + + + TextLabel + + + + + + + TextLabel + + + + + + + Total buffers size: + + + + + + + + + + Performance statistics + + + + + + TextLabel + + + + + + + TextLabel + + + + + + + TextLabel + + + + + + + TextLabel + + + + + + + Queued I/O jobs: + + + + + + + Write cache overload: + + + + + + + Average time in queue (ms): + + + + + + + Read cache overload: + + + + + + + Total queued size: + + + + + + + TextLabel + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + OK + + + + + + + + + +