Show overall statistics in plasmoid

This commit is contained in:
Martchus 2019-02-17 23:45:53 +01:00
parent 8690566992
commit e56c950100
12 changed files with 131 additions and 51 deletions

View File

@ -9,8 +9,8 @@ set(META_APP_DESCRIPTION "Tray application for Syncthing")
set(META_APP_CATEGORIES "Network;FileTransfer") set(META_APP_CATEGORIES "Network;FileTransfer")
set(META_GUI_OPTIONAL false) set(META_GUI_OPTIONAL false)
set(META_VERSION_MAJOR 0) set(META_VERSION_MAJOR 0)
set(META_VERSION_MINOR 8) set(META_VERSION_MINOR 9)
set(META_VERSION_PATCH 4) set(META_VERSION_PATCH 0)
set(META_VERSION_EXACT_SONAME ON) set(META_VERSION_EXACT_SONAME ON)
set(META_ADD_DEFAULT_CPP_UNIT_TEST_APPLICATION ON) set(META_ADD_DEFAULT_CPP_UNIT_TEST_APPLICATION ON)

View File

@ -251,3 +251,7 @@ on GitHub.
* The tray disconnects from the local instance when the network connection goes down. * The tray disconnects from the local instance when the network connection goes down.
The network connection must be restored or the tray restarted to be able to connect to local The network connection must be restored or the tray restarted to be able to connect to local
Syncthing again. This is caused by Qt bug https://bugreports.qt.io/browse/QTBUG-60949. Syncthing again. This is caused by Qt bug https://bugreports.qt.io/browse/QTBUG-60949.
## Attribution for 3rd party content
* This project uses icons from the Syncthing project.
* This project uses some icons from [Font Awesome](https://fontawesome.com) (see [their license](https://fontawesome.com/license)).

View File

@ -92,6 +92,7 @@ SyncthingConnection::SyncthingConnection(const QString &syncthingUrl, const QByt
, m_hasEvents(false) , m_hasEvents(false)
, m_hasDiskEvents(false) , m_hasDiskEvents(false)
, m_lastFileDeleted(false) , m_lastFileDeleted(false)
, m_dirStatsAltered(false)
{ {
m_trafficPollTimer.setInterval(SyncthingConnectionSettings::defaultTrafficPollInterval); m_trafficPollTimer.setInterval(SyncthingConnectionSettings::defaultTrafficPollInterval);
m_trafficPollTimer.setTimerType(Qt::VeryCoarseTimer); m_trafficPollTimer.setTimerType(Qt::VeryCoarseTimer);
@ -380,6 +381,8 @@ void SyncthingConnection::continueReconnecting()
m_lastFileName.clear(); m_lastFileName.clear();
m_lastFileDeleted = false; m_lastFileDeleted = false;
m_syncthingVersion.clear(); m_syncthingVersion.clear();
m_dirStatsAltered = false;
emit dirStatisticsChanged();
// notify that the configuration has been invalidated // notify that the configuration has been invalidated
if (!isConfigInvalidated) { if (!isConfigInvalidated) {
@ -427,6 +430,7 @@ void SyncthingConnection::concludeConnection()
return; return;
} }
setStatus(SyncthingStatus::Idle); setStatus(SyncthingStatus::Idle);
emitDirStatisticsChanged();
} }
/*! /*!
@ -868,6 +872,17 @@ void SyncthingConnection::emitMyIdChanged(const QString &newId)
emit myIdChanged(m_myId = newId); emit myIdChanged(m_myId = newId);
} }
/*!
* \brief Internally called to emit dirStatisticsChanged() event.
*/
void SyncthingConnection::emitDirStatisticsChanged()
{
if (m_dirStatsAltered) {
m_dirStatsAltered = false;
emit dirStatisticsChanged();
}
}
/*! /*!
* \brief Internally called to handle a fatal error when reading config (dirs/devs), status and events. * \brief Internally called to handle a fatal error when reading config (dirs/devs), status and events.
*/ */

View File

@ -219,6 +219,7 @@ Q_SIGNALS:
void dirStatusChanged(const SyncthingDir &dir, int index); void dirStatusChanged(const SyncthingDir &dir, int index);
void devStatusChanged(const SyncthingDev &dev, int index); void devStatusChanged(const SyncthingDev &dev, int index);
void downloadProgressChanged(); void downloadProgressChanged();
void dirStatisticsChanged();
void dirCompleted(ChronoUtilities::DateTime when, const SyncthingDir &dir, int index, const SyncthingDev *remoteDev = nullptr); void dirCompleted(ChronoUtilities::DateTime when, const SyncthingDir &dir, int index, const SyncthingDev *remoteDev = nullptr);
void newNotification(ChronoUtilities::DateTime when, const QString &message); void newNotification(ChronoUtilities::DateTime when, const QString &message);
void newDevAvailable(ChronoUtilities::DateTime when, const QString &devId, const QString &address); void newDevAvailable(ChronoUtilities::DateTime when, const QString &devId, const QString &address);
@ -298,6 +299,7 @@ private Q_SLOTS:
void emitError(const QString &message, const QJsonParseError &jsonError, QNetworkReply *reply, const QByteArray &response = QByteArray()); void emitError(const QString &message, const QJsonParseError &jsonError, QNetworkReply *reply, const QByteArray &response = QByteArray());
void emitError(const QString &message, SyncthingErrorCategory category, QNetworkReply *reply); void emitError(const QString &message, SyncthingErrorCategory category, QNetworkReply *reply);
void emitMyIdChanged(const QString &newId); void emitMyIdChanged(const QString &newId);
void emitDirStatisticsChanged();
void handleFatalConnectionError(); void handleFatalConnectionError();
void handleAdditionalRequestCanceled(); void handleAdditionalRequestCanceled();
void recalculateStatus(); void recalculateStatus();
@ -364,6 +366,7 @@ private:
bool m_lastFileDeleted; bool m_lastFileDeleted;
QList<QSslError> m_expectedSslErrors; QList<QSslError> m_expectedSslErrors;
QJsonObject m_rawConfig; QJsonObject m_rawConfig;
bool m_dirStatsAltered;
}; };
/*! /*!

View File

@ -1411,6 +1411,7 @@ void SyncthingConnection::readDirSummary(DateTime eventTime, const QJsonObject &
neededStats.dirs = jsonValueToInt(summary.value(QLatin1String("needDirectories"))); neededStats.dirs = jsonValueToInt(summary.value(QLatin1String("needDirectories")));
neededStats.symlinks = jsonValueToInt(summary.value(QLatin1String("needSymlinks"))); neededStats.symlinks = jsonValueToInt(summary.value(QLatin1String("needSymlinks")));
dir.pullErrorCount = jsonValueToInt(summary.value(QLatin1String("pullErrors"))); dir.pullErrorCount = jsonValueToInt(summary.value(QLatin1String("pullErrors")));
m_dirStatsAltered = true;
dir.ignorePatterns = summary.value(QLatin1String("ignorePatterns")).toBool(); dir.ignorePatterns = summary.value(QLatin1String("ignorePatterns")).toBool();
dir.lastStatisticsUpdate = eventTime; dir.lastStatisticsUpdate = eventTime;
@ -1608,6 +1609,7 @@ void SyncthingConnection::readEventsFromJsonArray(const QJsonArray &events, int
readChangeEvent(eventTime, eventType, eventData); readChangeEvent(eventTime, eventType, eventData);
} }
} }
emitDirStatisticsChanged();
} }
/*! /*!

View File

@ -27,7 +27,7 @@ find_package(c++utilities 4.10.0 REQUIRED)
list(APPEND CMAKE_MODULE_PATH ${CPP_UTILITIES_MODULE_DIRS}) list(APPEND CMAKE_MODULE_PATH ${CPP_UTILITIES_MODULE_DIRS})
# find qtutilities # find qtutilities
find_package(qtutilities 5.12.0 REQUIRED) find_package(qtutilities 5.13.0 REQUIRED)
use_qt_utilities() use_qt_utilities()
# check whether qtutilities supports DBus notifications # check whether qtutilities supports DBus notifications

View File

@ -88,6 +88,7 @@ void SyncthingApplet::init()
connect(&m_connection, &SyncthingConnection::devStatusChanged, this, &SyncthingApplet::handleDevicesChanged); connect(&m_connection, &SyncthingConnection::devStatusChanged, this, &SyncthingApplet::handleDevicesChanged);
connect(&m_connection, &SyncthingConnection::error, this, &SyncthingApplet::handleInternalError); connect(&m_connection, &SyncthingConnection::error, this, &SyncthingApplet::handleInternalError);
connect(&m_connection, &SyncthingConnection::trafficChanged, this, &SyncthingApplet::trafficChanged); connect(&m_connection, &SyncthingConnection::trafficChanged, this, &SyncthingApplet::trafficChanged);
connect(&m_connection, &SyncthingConnection::dirStatisticsChanged, this, &SyncthingApplet::handleDirStatisticsChanged);
connect(&m_connection, &SyncthingConnection::newNotification, this, &SyncthingApplet::handleNewNotification); connect(&m_connection, &SyncthingConnection::newNotification, this, &SyncthingApplet::handleNewNotification);
connect(&m_notifier, &SyncthingNotifier::newDevice, &m_dbusNotifier, &DBusStatusNotifier::showNewDev); connect(&m_notifier, &SyncthingNotifier::newDevice, &m_dbusNotifier, &DBusStatusNotifier::showNewDev);
connect(&m_notifier, &SyncthingNotifier::newDir, &m_dbusNotifier, &DBusStatusNotifier::showNewDir); connect(&m_notifier, &SyncthingNotifier::newDir, &m_dbusNotifier, &DBusStatusNotifier::showNewDir);
@ -129,11 +130,31 @@ QString SyncthingApplet::incomingTraffic() const
return trafficString(m_connection.totalIncomingTraffic(), m_connection.totalIncomingRate()); return trafficString(m_connection.totalIncomingTraffic(), m_connection.totalIncomingRate());
} }
bool SyncthingApplet::hasIncomingTraffic() const
{
return m_connection.totalIncomingRate() > 0.0;
}
QString SyncthingApplet::outgoingTraffic() const QString SyncthingApplet::outgoingTraffic() const
{ {
return trafficString(m_connection.totalOutgoingTraffic(), m_connection.totalOutgoingRate()); return trafficString(m_connection.totalOutgoingTraffic(), m_connection.totalOutgoingRate());
} }
bool SyncthingApplet::hasOutgoingTraffic() const
{
return m_connection.totalOutgoingRate() > 0.0;
}
QString SyncthingApplet::globalStatistics() const
{
return directoryStatusString(m_overallStats.global);
}
QString SyncthingApplet::localStatistics() const
{
return directoryStatusString(m_overallStats.local);
}
QStringList SyncthingApplet::connectionConfigNames() const QStringList SyncthingApplet::connectionConfigNames() const
{ {
const auto &settings = Settings::values().connection; const auto &settings = Settings::values().connection;
@ -273,7 +294,10 @@ void SyncthingApplet::showOwnDeviceId()
void SyncthingApplet::showAboutDialog() void SyncthingApplet::showAboutDialog()
{ {
if (!m_aboutDlg) { if (!m_aboutDlg) {
m_aboutDlg = new AboutDialog(nullptr, QStringLiteral(APP_NAME), QStringLiteral(APP_AUTHOR "\nSyncthing icons from Syncthing project"), m_aboutDlg = new AboutDialog(nullptr, QStringLiteral(APP_NAME),
QStringLiteral("<p>Developed by " APP_AUTHOR "<br>Syncthing icons from <a href=\"https://syncthing.net\">Syncthing project</a><br>Using "
"icons from <a href=\"https://fontawesome.com\">Font "
"Awesome</a> (see <a href=\"https://fontawesome.com/license\">their license</a>)</p>"),
QStringLiteral(APP_VERSION), ApplicationUtilities::dependencyVersions2, QStringLiteral(APP_URL), QStringLiteral(APP_DESCRIPTION), QStringLiteral(APP_VERSION), ApplicationUtilities::dependencyVersions2, QStringLiteral(APP_URL), QStringLiteral(APP_DESCRIPTION),
QImage(statusIcons().scanninig.pixmap(128).toImage())); QImage(statusIcons().scanninig.pixmap(128).toImage()));
m_aboutDlg->setWindowTitle(tr("About") + QStringLiteral(" - " APP_NAME)); m_aboutDlg->setWindowTitle(tr("About") + QStringLiteral(" - " APP_NAME));
@ -405,6 +429,12 @@ void SyncthingApplet::handleInternalError(
InternalErrorsDialog::addError(move(error)); InternalErrorsDialog::addError(move(error));
} }
void SyncthingApplet::handleDirStatisticsChanged()
{
m_overallStats = m_connection.computeOverallDirStatistics();
emit statisticsChanged();
}
void SyncthingApplet::handleErrorsCleared() void SyncthingApplet::handleErrorsCleared()
{ {
} }

View File

@ -52,7 +52,11 @@ class SyncthingApplet : public Plasma::Applet {
Q_PROPERTY(QString additionalStatusText READ additionalStatusText NOTIFY connectionStatusChanged) Q_PROPERTY(QString additionalStatusText READ additionalStatusText NOTIFY connectionStatusChanged)
Q_PROPERTY(QIcon statusIcon READ statusIcon NOTIFY connectionStatusChanged) Q_PROPERTY(QIcon statusIcon READ statusIcon NOTIFY connectionStatusChanged)
Q_PROPERTY(QString incomingTraffic READ incomingTraffic NOTIFY trafficChanged) Q_PROPERTY(QString incomingTraffic READ incomingTraffic NOTIFY trafficChanged)
Q_PROPERTY(bool hasIncomingTraffic READ hasIncomingTraffic NOTIFY trafficChanged)
Q_PROPERTY(QString outgoingTraffic READ outgoingTraffic NOTIFY trafficChanged) Q_PROPERTY(QString outgoingTraffic READ outgoingTraffic NOTIFY trafficChanged)
Q_PROPERTY(bool hasOutgoingTraffic READ hasOutgoingTraffic NOTIFY trafficChanged)
Q_PROPERTY(QString globalStatistics READ globalStatistics NOTIFY statisticsChanged)
Q_PROPERTY(QString localStatistics READ localStatistics NOTIFY statisticsChanged)
Q_PROPERTY(QStringList connectionConfigNames READ connectionConfigNames NOTIFY settingsChanged) Q_PROPERTY(QStringList connectionConfigNames READ connectionConfigNames NOTIFY settingsChanged)
Q_PROPERTY(QString currentConnectionConfigName READ currentConnectionConfigName NOTIFY currentConnectionConfigIndexChanged) Q_PROPERTY(QString currentConnectionConfigName READ currentConnectionConfigName NOTIFY currentConnectionConfigIndexChanged)
Q_PROPERTY(int currentConnectionConfigIndex READ currentConnectionConfigIndex WRITE setCurrentConnectionConfigIndex NOTIFY Q_PROPERTY(int currentConnectionConfigIndex READ currentConnectionConfigIndex WRITE setCurrentConnectionConfigIndex NOTIFY
@ -79,7 +83,11 @@ public:
QString additionalStatusText() const; QString additionalStatusText() const;
QIcon statusIcon() const; QIcon statusIcon() const;
QString incomingTraffic() const; QString incomingTraffic() const;
bool hasIncomingTraffic() const;
QString outgoingTraffic() const; QString outgoingTraffic() const;
bool hasOutgoingTraffic() const;
QString globalStatistics() const;
QString localStatistics() const;
QStringList connectionConfigNames() const; QStringList connectionConfigNames() const;
QString currentConnectionConfigName() const; QString currentConnectionConfigName() const;
int currentConnectionConfigIndex() const; int currentConnectionConfigIndex() const;
@ -124,6 +132,7 @@ Q_SIGNALS:
void localChanged(); void localChanged();
void connectionStatusChanged(); void connectionStatusChanged();
void trafficChanged(); void trafficChanged();
void statisticsChanged();
void settingsChanged(); void settingsChanged();
void currentConnectionConfigIndexChanged(int index); void currentConnectionConfigIndexChanged(int index);
void sizeChanged(const QSize &size); void sizeChanged(const QSize &size);
@ -136,6 +145,7 @@ private Q_SLOTS:
void handleDevicesChanged(); void handleDevicesChanged();
void handleInternalError( void handleInternalError(
const QString &errorMsg, Data::SyncthingErrorCategory category, int networkError, const QNetworkRequest &request, const QByteArray &response); const QString &errorMsg, Data::SyncthingErrorCategory category, int networkError, const QNetworkRequest &request, const QByteArray &response);
void handleDirStatisticsChanged();
void handleErrorsCleared(); void handleErrorsCleared();
void handleAboutDialogDeleted(); void handleAboutDialogDeleted();
#ifndef SYNCTHINGWIDGETS_NO_WEBVIEW #ifndef SYNCTHINGWIDGETS_NO_WEBVIEW
@ -151,6 +161,7 @@ private Q_SLOTS:
private: private:
Dialogs::AboutDialog *m_aboutDlg; Dialogs::AboutDialog *m_aboutDlg;
Data::SyncthingConnection m_connection; Data::SyncthingConnection m_connection;
Data::SyncthingOverallDirStatistics m_overallStats;
Data::SyncthingNotifier m_notifier; Data::SyncthingNotifier m_notifier;
#ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD #ifdef LIB_SYNCTHING_CONNECTOR_SUPPORT_SYSTEMD
Data::SyncthingService m_service; Data::SyncthingService m_service;

View File

@ -378,53 +378,6 @@ ColumnLayout {
onActivated: webUIButton.clicked() onActivated: webUIButton.clicked()
} }
} }
}
PlasmaCore.SvgItem {
Layout.preferredWidth: parent.width
Layout.preferredHeight: 2
elementId: "horizontal-line"
svg: PlasmaCore.Svg {
imagePath: "widgets/line"
}
}
// traffic and connection selection
RowLayout {
Layout.fillWidth: true
Layout.fillHeight: false
PlasmaCore.IconItem {
source: "network-card"
Layout.preferredWidth: 32
Layout.preferredHeight: 32
}
ColumnLayout {
Layout.fillHeight: true
spacing: 1
PlasmaComponents.Label {
text: qsTr("In")
}
PlasmaComponents.Label {
text: qsTr("Out")
}
}
ColumnLayout {
Layout.fillHeight: true
spacing: 1
PlasmaComponents.Label {
text: plasmoid.nativeInterface.incomingTraffic
}
PlasmaComponents.Label {
text: plasmoid.nativeInterface.outgoingTraffic
}
}
Item {
Layout.fillWidth: true
Layout.fillHeight: true
}
TinyButton { TinyButton {
text: plasmoid.nativeInterface.currentConnectionConfigName text: plasmoid.nativeInterface.currentConnectionConfigName
iconSource: "network-connect" iconSource: "network-connect"
@ -467,6 +420,64 @@ ColumnLayout {
} }
} }
// global statistics and traffic
GridLayout {
Layout.fillWidth: true
Layout.fillHeight: false
columns: 4
rowSpacing: 1
columnSpacing: 4
PlasmaCore.IconItem {
Layout.preferredWidth: 16
Layout.preferredHeight: 16
source: "globe"
}
PlasmaComponents.Label {
text: plasmoid.nativeInterface.globalStatistics
}
PlasmaCore.IconItem {
Layout.preferredWidth: 16
Layout.preferredHeight: 16
Layout.leftMargin: 10
source: "://icons/hicolor/scalable/fa/cloud-download-alt-solid.svg"
opacity: plasmoid.nativeInterface.hasIncomingTraffic ? 1.0 : 0.5
}
PlasmaComponents.Label {
text: plasmoid.nativeInterface.incomingTraffic
}
PlasmaCore.IconItem {
Layout.preferredWidth: 16
Layout.preferredHeight: 16
source: "user-home-symbolic"
}
PlasmaComponents.Label {
text: plasmoid.nativeInterface.localStatistics
}
PlasmaCore.IconItem {
Layout.preferredWidth: 16
Layout.preferredHeight: 16
Layout.leftMargin: 10
source: "://icons/hicolor/scalable/fa/cloud-upload-alt-solid.svg"
opacity: plasmoid.nativeInterface.hasOutgoingTraffic ? 1.0 : 0.5
}
PlasmaComponents.Label {
text: plasmoid.nativeInterface.outgoingTraffic
}
}
PlasmaCore.SvgItem {
Layout.preferredWidth: parent.width
Layout.preferredHeight: 2
elementId: "horizontal-line"
svg: PlasmaCore.Svg {
imagePath: "widgets/line"
}
}
// tab "widget" // tab "widget"
RowLayout { RowLayout {
spacing: 0 spacing: 0

View File

@ -0,0 +1 @@
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="cloud-download-alt" class="svg-inline--fa fa-cloud-download-alt fa-w-20" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="currentColor" d="M537.6 226.6c4.1-10.7 6.4-22.4 6.4-34.6 0-53-43-96-96-96-19.7 0-38.1 6-53.3 16.2C367 64.2 315.3 32 256 32c-88.4 0-160 71.6-160 160 0 2.7.1 5.4.2 8.1C40.2 219.8 0 273.2 0 336c0 79.5 64.5 144 144 144h368c70.7 0 128-57.3 128-128 0-61.9-44-113.6-102.4-125.4zm-132.9 88.7L299.3 420.7c-6.2 6.2-16.4 6.2-22.6 0L171.3 315.3c-10.1-10.1-2.9-27.3 11.3-27.3H248V176c0-8.8 7.2-16 16-16h48c8.8 0 16 7.2 16 16v112h65.4c14.2 0 21.4 17.2 11.3 27.3z"></path></svg>

After

Width:  |  Height:  |  Size: 687 B

View File

@ -0,0 +1 @@
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="cloud-upload-alt" class="svg-inline--fa fa-cloud-upload-alt fa-w-20" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="currentColor" d="M537.6 226.6c4.1-10.7 6.4-22.4 6.4-34.6 0-53-43-96-96-96-19.7 0-38.1 6-53.3 16.2C367 64.2 315.3 32 256 32c-88.4 0-160 71.6-160 160 0 2.7.1 5.4.2 8.1C40.2 219.8 0 273.2 0 336c0 79.5 64.5 144 144 144h368c70.7 0 128-57.3 128-128 0-61.9-44-113.6-102.4-125.4zM393.4 288H328v112c0 8.8-7.2 16-16 16h-48c-8.8 0-16-7.2-16-16V288h-65.4c-14.3 0-21.4-17.2-11.3-27.3l105.4-105.4c6.2-6.2 16.4-6.2 22.6 0l105.4 105.4c10.1 10.1 2.9 27.3-11.3 27.3z"></path></svg>

After

Width:  |  Height:  |  Size: 683 B

View File

@ -9,5 +9,7 @@
<file>icons/hicolor/scalable/actions/list-remove.svg</file> <file>icons/hicolor/scalable/actions/list-remove.svg</file>
<file>icons/hicolor/scalable/actions/edit-paste.svg</file> <file>icons/hicolor/scalable/actions/edit-paste.svg</file>
<file>icons/hicolor/scalable/app/syncthingtray.svg</file> <file>icons/hicolor/scalable/app/syncthingtray.svg</file>
<file>icons/hicolor/scalable/fa/cloud-download-alt-solid.svg</file>
<file>icons/hicolor/scalable/fa/cloud-upload-alt-solid.svg</file>
</qresource> </qresource>
</RCC> </RCC>