/* BEGIN_COMMON_COPYRIGHT_HEADER * (c)LGPL2+ * * LXDE-Qt - a lightweight, Qt based, desktop toolset * http://razor-qt.org * * Copyright: 2012 Razor team * Authors: * Kuzma Shapran * * This program or library is free software; you can redistribute it * and/or modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA * * END_COMMON_COPYRIGHT_HEADER */ #include "lxqtsysstat.h" #include "lxqtsysstatutils.h" #include #include #include #include #include #include #include #include #include LXQtSysStat::LXQtSysStat(const ILXQtPanelPluginStartupInfo &startupInfo): QObject(), ILXQtPanelPlugin(startupInfo), mWidget(new QWidget()), mFakeTitle(new LXQtSysStatTitle(mWidget)), mContent(new LXQtSysStatContent(this, mWidget)) { QVBoxLayout *borderLayout = new QVBoxLayout(mWidget); borderLayout->setContentsMargins(0, 0, 0, 0); borderLayout->setSpacing(0); borderLayout->addWidget(mContent); borderLayout->setStretchFactor(mContent, 1); mContent->setMinimumSize(2, 2); // qproperty of font type doesn't work with qss, so fake QLabel is used instead connect(mFakeTitle, SIGNAL(fontChanged(QFont)), mContent, SLOT(setTitleFont(QFont))); // has to be postponed to update the size first QTimer::singleShot(0, this, SLOT(lateInit())); } LXQtSysStat::~LXQtSysStat() { delete mWidget; } void LXQtSysStat::lateInit() { settingsChanged(); mContent->setTitleFont(mFakeTitle->font()); mSize = mContent->size(); } QDialog *LXQtSysStat::configureDialog() { return new LXQtSysStatConfiguration(settings(), mWidget); } void LXQtSysStat::realign() { QSize newSize = mContent->size(); if (mSize != newSize) { mContent->reset(); mSize = newSize; } } void LXQtSysStat::settingsChanged() { mContent->updateSettings(settings()); } LXQtSysStatTitle::LXQtSysStatTitle(QWidget *parent): QLabel(parent) { } LXQtSysStatTitle::~LXQtSysStatTitle() { } bool LXQtSysStatTitle::event(QEvent *e) { if (e->type() == QEvent::FontChange) emit fontChanged(font()); return QLabel::event(e); } LXQtSysStatContent::LXQtSysStatContent(ILXQtPanelPlugin *plugin, QWidget *parent): QWidget(parent), mPlugin(plugin), mStat(NULL), mUpdateInterval(0), mMinimalSize(0), mTitleFontPixelHeight(0), mUseThemeColours(true), mHistoryOffset(0) { setObjectName("SysStat_Graph"); } LXQtSysStatContent::~LXQtSysStatContent() { } // I don't like macros very much, but writing dozen similar functions is much much worse. #undef QSS_GET_COLOUR #define QSS_GET_COLOUR(GETNAME) \ QColor LXQtSysStatContent::GETNAME##Colour() const \ { \ return mThemeColours.GETNAME##Colour; \ } #undef QSS_COLOUR #define QSS_COLOUR(GETNAME, SETNAME) \ QSS_GET_COLOUR(GETNAME) \ void LXQtSysStatContent::SETNAME##Colour(QColor value) \ { \ mThemeColours.GETNAME##Colour = value; \ if (mUseThemeColours) \ mColours.GETNAME##Colour = mThemeColours.GETNAME##Colour; \ } #undef QSS_NET_COLOUR #define QSS_NET_COLOUR(GETNAME, SETNAME) \ QSS_GET_COLOUR(GETNAME) \ void LXQtSysStatContent::SETNAME##Colour(QColor value) \ { \ mThemeColours.GETNAME##Colour = value; \ if (mUseThemeColours) \ { \ mColours.GETNAME##Colour = mThemeColours.GETNAME##Colour; \ mixNetColours(); \ } \ } QSS_COLOUR(grid, setGrid) QSS_COLOUR(title, setTitle) QSS_COLOUR(cpuSystem, setCpuSystem) QSS_COLOUR(cpuUser, setCpuUser) QSS_COLOUR(cpuNice, setCpuNice) QSS_COLOUR(cpuOther, setCpuOther) QSS_COLOUR(frequency, setFrequency) QSS_COLOUR(memApps, setMemApps) QSS_COLOUR(memBuffers,setMemBuffers) QSS_COLOUR(memCached, setMemCached) QSS_COLOUR(swapUsed, setSwapUsed) QSS_NET_COLOUR(netReceived, setNetReceived) QSS_NET_COLOUR(netTransmitted, setNetTransmitted) #undef QSS_NET_COLOUR #undef QSS_COLOUR #undef QSS_GET_COLOUR void LXQtSysStatContent::mixNetColours() { QColor netReceivedColour_hsv = mColours.netReceivedColour.toHsv(); QColor netTransmittedColour_hsv = mColours.netTransmittedColour.toHsv(); qreal hue = (netReceivedColour_hsv.hueF() + netTransmittedColour_hsv.hueF()) / 2; if (qAbs(netReceivedColour_hsv.hueF() - netTransmittedColour_hsv.hueF()) > 0.5) hue += 0.5; mNetBothColour.setHsvF( hue, (netReceivedColour_hsv.saturationF() + netTransmittedColour_hsv.saturationF()) / 2, (netReceivedColour_hsv.valueF() + netTransmittedColour_hsv.valueF() ) / 2 ); } void LXQtSysStatContent::setTitleFont(QFont value) { mTitleFont = value; updateTitleFontPixelHeight(); update(); } void LXQtSysStatContent::updateTitleFontPixelHeight() { if (mTitleLabel.isEmpty()) mTitleFontPixelHeight = 0; else { QFontMetrics fm(mTitleFont); mTitleFontPixelHeight = fm.height() - 1; } } void LXQtSysStatContent::updateSettings(const PluginSettings *settings) { double old_updateInterval = mUpdateInterval; int old_minimalSize = mMinimalSize; QString old_dataType = mDataType; QString old_dataSource = mDataSource; bool old_useFrequency = mUseFrequency; bool old_logarithmicScale = mLogarithmicScale; int old_logScaleSteps = mLogScaleSteps; mUseThemeColours = settings->value("graph/useThemeColours", true).toBool(); mUpdateInterval = settings->value("graph/updateInterval", 1.0).toDouble(); mMinimalSize = settings->value("graph/minimalSize", 30).toInt(); mGridLines = settings->value("grid/lines", 1).toInt(); mTitleLabel = settings->value("title/label", QString()).toString(); // default to CPU monitoring mDataType = settings->value("data/type", LXQtSysStatConfiguration::msStatTypes[0]).toString(); mDataSource = settings->value("data/source", QString("cpu")).toString(); mUseFrequency = settings->value("cpu/useFrequency", true).toBool(); mNetMaximumSpeed = PluginSysStat::netSpeedFromString(settings->value("net/maximumSpeed", "1 MB/s").toString()); mLogarithmicScale = settings->value("net/logarithmicScale", true).toBool(); mLogScaleSteps = settings->value("net/logarithmicScaleSteps", 4).toInt(); mLogScaleMax = static_cast(static_cast(1) << mLogScaleSteps); mNetRealMaximumSpeed = static_cast(static_cast(1) << mNetMaximumSpeed); mSettingsColours.gridColour = QColor(settings->value("grid/colour", "#c0c0c0").toString()); mSettingsColours.titleColour = QColor(settings->value("title/colour", "#ffffff").toString()); mSettingsColours.cpuSystemColour = QColor(settings->value("cpu/systemColour", "#800000").toString()); mSettingsColours.cpuUserColour = QColor(settings->value("cpu/userColour", "#000080").toString()); mSettingsColours.cpuNiceColour = QColor(settings->value("cpu/niceColour", "#008000").toString()); mSettingsColours.cpuOtherColour = QColor(settings->value("cpu/otherColour", "#808000").toString()); mSettingsColours.frequencyColour = QColor(settings->value("cpu/frequencyColour", "#808080").toString()); mSettingsColours.memAppsColour = QColor(settings->value("mem/appsColour", "#000080").toString()); mSettingsColours.memBuffersColour = QColor(settings->value("mem/buffersColour", "#008000").toString()); mSettingsColours.memCachedColour = QColor(settings->value("mem/cachedColour", "#808000").toString()); mSettingsColours.swapUsedColour = QColor(settings->value("mem/swapColour", "#800000").toString()); mSettingsColours.netReceivedColour = QColor(settings->value("net/receivedColour", "#000080").toString()); mSettingsColours.netTransmittedColour = QColor(settings->value("net/transmittedColour", "#808000").toString()); if (mUseThemeColours) mColours = mThemeColours; else mColours = mSettingsColours; mixNetColours(); updateTitleFontPixelHeight(); bool minimalSizeChanged = old_minimalSize != mMinimalSize; bool updateIntervalChanged = old_updateInterval != mUpdateInterval; bool dataTypeChanged = old_dataType != mDataType; bool dataSourceChanged = old_dataSource != mDataSource; bool useFrequencyChanged = old_useFrequency != mUseFrequency; bool logScaleStepsChanged = old_logScaleSteps != mLogScaleSteps; bool logarithmicScaleChanged = old_logarithmicScale != mLogarithmicScale; bool needReconnecting = dataTypeChanged || dataSourceChanged || useFrequencyChanged; bool needTimerRestarting = needReconnecting || updateIntervalChanged; bool needFullReset = needTimerRestarting || minimalSizeChanged || logScaleStepsChanged || logarithmicScaleChanged; if (mStat) { if (needTimerRestarting) mStat->stopUpdating(); if (needReconnecting) mStat->disconnect(this); } if (dataTypeChanged) { if (mStat) { mStat->deleteLater(); mStat = nullptr; } if (mDataType == "CPU") mStat = new SysStat::CpuStat(this); else if (mDataType == "Memory") mStat = new SysStat::MemStat(this); else if (mDataType == "Network") mStat = new SysStat::NetStat(this); } if (mStat) { if (needReconnecting) { if (mDataType == "CPU") { if (mUseFrequency) { qobject_cast(mStat)->setMonitoring(SysStat::CpuStat::LoadAndFrequency); connect(qobject_cast(mStat), SIGNAL(update(float, float, float, float, float, uint)), this, SLOT(cpuUpdate(float, float, float, float, float, uint))); } else { qobject_cast(mStat)->setMonitoring(SysStat::CpuStat::LoadOnly); connect(qobject_cast(mStat), SIGNAL(update(float, float, float, float)), this, SLOT(cpuUpdate(float, float, float, float))); } } else if (mDataType == "Memory") { if (mDataSource == "memory") connect(qobject_cast(mStat), SIGNAL(memoryUpdate(float, float, float)), this, SLOT(memoryUpdate(float, float, float))); else connect(qobject_cast(mStat), SIGNAL(swapUpdate(float)), this, SLOT(swapUpdate(float))); } else if (mDataType == "Network") { connect(qobject_cast(mStat), SIGNAL(update(unsigned, unsigned)), this, SLOT(networkUpdate(unsigned, unsigned))); } mStat->setMonitoredSource(mDataSource); } if (needTimerRestarting) mStat->setUpdateInterval(static_cast(mUpdateInterval * 1000.0)); } if (needFullReset) reset(); else update(); } void LXQtSysStatContent::resizeEvent(QResizeEvent * /*event*/) { reset(); } void LXQtSysStatContent::reset() { setMinimumSize(mPlugin->panel()->isHorizontal() ? mMinimalSize : 2, mPlugin->panel()->isHorizontal() ? 2 : mMinimalSize); mHistoryOffset = 0; mHistoryImage = QImage(width(), 100, QImage::Format_ARGB32); mHistoryImage.fill(Qt::transparent); update(); } template T clamp(const T &value, const T &min, const T &max) { return qMin(qMax(value, min), max); } // QPainter.drawLine with pen set to Qt::transparent doesn't clear anything void LXQtSysStatContent::clearLine() { QRgb bg = QColor(Qt::transparent).rgba(); for (int i = 0; i < 100; ++i) reinterpret_cast(mHistoryImage.scanLine(i))[mHistoryOffset] = bg; } void LXQtSysStatContent::cpuUpdate(float user, float nice, float system, float other, float frequencyRate, uint) { int y_system = static_cast(system * 100.0 * frequencyRate); int y_user = static_cast(user * 100.0 * frequencyRate); int y_nice = static_cast(nice * 100.0 * frequencyRate); int y_other = static_cast(other * 100.0 * frequencyRate); int y_freq = static_cast( 100.0 * frequencyRate); toolTipInfo(tr("system: %1%
user: %2%
nice: %3%
other: %4%
freq: %5%", "CPU tooltip information") .arg(y_system).arg(y_user).arg(y_nice).arg(y_other).arg(y_freq)); y_system = clamp(y_system, 0, 99); y_user = clamp(y_user + y_system, 0, 99); y_nice = clamp(y_nice + y_user , 0, 99); y_other = clamp(y_other, 0, 99); y_freq = clamp(y_freq, 0, 99); clearLine(); QPainter painter(&mHistoryImage); if (y_system != 0) { painter.setPen(mColours.cpuSystemColour); painter.drawLine(mHistoryOffset, y_system, mHistoryOffset, 0); } if (y_user != y_system) { painter.setPen(mColours.cpuUserColour); painter.drawLine(mHistoryOffset, y_user, mHistoryOffset, y_system); } if (y_nice != y_user) { painter.setPen(mColours.cpuNiceColour); painter.drawLine(mHistoryOffset, y_nice, mHistoryOffset, y_user); } if (y_other != y_nice) { painter.setPen(mColours.cpuOtherColour); painter.drawLine(mHistoryOffset, y_other, mHistoryOffset, y_nice); } if (y_freq != y_other) { painter.setPen(mColours.frequencyColour); painter.drawLine(mHistoryOffset, y_freq, mHistoryOffset, y_other); } mHistoryOffset = (mHistoryOffset + 1) % width(); update(0, mTitleFontPixelHeight, width(), height() - mTitleFontPixelHeight); } void LXQtSysStatContent::cpuUpdate(float user, float nice, float system, float other) { int y_system = static_cast(system * 100.0); int y_user = static_cast(user * 100.0); int y_nice = static_cast(nice * 100.0); int y_other = static_cast(other * 100.0); toolTipInfo(tr("system: %1%
user: %2%
nice: %3%
other: %4%
freq: n/a", "CPU tooltip information") .arg(y_system).arg(y_user).arg(y_nice).arg(y_other)); y_system = clamp(y_system, 0, 99); y_user = clamp(y_user + y_system, 0, 99); y_nice = clamp(y_nice + y_user, 0, 99); y_other = clamp(y_other + y_nice, 0, 99); clearLine(); QPainter painter(&mHistoryImage); if (y_system != 0) { painter.setPen(mColours.cpuSystemColour); painter.drawLine(mHistoryOffset, y_system, mHistoryOffset, 0); } if (y_user != y_system) { painter.setPen(mColours.cpuUserColour); painter.drawLine(mHistoryOffset, y_user, mHistoryOffset, y_system); } if (y_nice != y_user) { painter.setPen(mColours.cpuNiceColour); painter.drawLine(mHistoryOffset, y_nice, mHistoryOffset, y_user); } if (y_other != y_nice) { painter.setPen(mColours.cpuOtherColour); painter.drawLine(mHistoryOffset, y_other, mHistoryOffset, y_nice); } mHistoryOffset = (mHistoryOffset + 1) % width(); update(0, mTitleFontPixelHeight, width(), height() - mTitleFontPixelHeight); } void LXQtSysStatContent::memoryUpdate(float apps, float buffers, float cached) { int y_apps = static_cast(apps * 100.0); int y_buffers = static_cast(buffers * 100.0); int y_cached = static_cast(cached * 100.0); toolTipInfo(tr("apps: %1%
buffers: %2%
cached: %3%", "Memory tooltip information") .arg(y_apps).arg(y_buffers).arg(y_cached)); y_apps = clamp(y_apps, 0, 99); y_buffers = clamp(y_buffers + y_apps, 0, 99); y_cached = clamp(y_cached + y_buffers, 0, 99); clearLine(); QPainter painter(&mHistoryImage); if (y_apps != 0) { painter.setPen(mColours.memAppsColour); painter.drawLine(mHistoryOffset, y_apps, mHistoryOffset, 0); } if (y_buffers != y_apps) { painter.setPen(mColours.memBuffersColour); painter.drawLine(mHistoryOffset, y_buffers, mHistoryOffset, y_apps); } if (y_cached != y_buffers) { painter.setPen(mColours.memCachedColour); painter.drawLine(mHistoryOffset, y_cached, mHistoryOffset, y_buffers); } mHistoryOffset = (mHistoryOffset + 1) % width(); update(0, mTitleFontPixelHeight, width(), height() - mTitleFontPixelHeight); } void LXQtSysStatContent::swapUpdate(float used) { int y_used = static_cast(used * 100.0); toolTipInfo(tr("used: %1%", "Swap tooltip information").arg(y_used)); y_used = clamp(y_used, 0, 99); clearLine(); QPainter painter(&mHistoryImage); if (y_used != 0) { painter.setPen(mColours.swapUsedColour); painter.drawLine(mHistoryOffset, y_used, mHistoryOffset, 0); } mHistoryOffset = (mHistoryOffset + 1) % width(); update(0, mTitleFontPixelHeight, width(), height() - mTitleFontPixelHeight); } void LXQtSysStatContent::networkUpdate(unsigned received, unsigned transmitted) { qreal min_value = qMin(qMax(static_cast(qMin(received, transmitted)) / mNetRealMaximumSpeed, static_cast(0.0)), static_cast(1.0)); qreal max_value = qMin(qMax(static_cast(qMax(received, transmitted)) / mNetRealMaximumSpeed, static_cast(0.0)), static_cast(1.0)); if (mLogarithmicScale) { min_value = qLn(min_value * (mLogScaleMax - 1.0) + 1.0) / qLn(2.0) / static_cast(mLogScaleSteps); max_value = qLn(max_value * (mLogScaleMax - 1.0) + 1.0) / qLn(2.0) / static_cast(mLogScaleSteps); } int y_min_value = static_cast(min_value * 100.0); int y_max_value = static_cast(max_value * 100.0); toolTipInfo(tr("min: %1%
max: %2%", "Network tooltip information").arg(y_min_value).arg(y_max_value)); y_min_value = clamp(y_min_value, 0, 99); y_max_value = clamp(y_max_value + y_min_value, 0, 99); clearLine(); QPainter painter(&mHistoryImage); if (y_min_value != 0) { painter.setPen(mNetBothColour); painter.drawLine(mHistoryOffset, y_min_value, mHistoryOffset, 0); } if (y_max_value != y_min_value) { painter.setPen((received > transmitted) ? mColours.netReceivedColour : mColours.netTransmittedColour); painter.drawLine(mHistoryOffset, y_max_value, mHistoryOffset, y_min_value); } mHistoryOffset = (mHistoryOffset + 1) % width(); update(0, mTitleFontPixelHeight, width(), height() - mTitleFontPixelHeight); } void LXQtSysStatContent::paintEvent(QPaintEvent *event) { QPainter p(this); qreal graphTop = 0; qreal graphHeight = height(); bool hasTitle = !mTitleLabel.isEmpty(); if (hasTitle) { graphTop = mTitleFontPixelHeight; graphHeight -= graphTop; if (event->region().intersects(QRect(0, 0, width(), graphTop))) { p.setPen(mColours.titleColour); p.setFont(mTitleFont); p.drawText(QRectF(0, 0, width(), graphTop), Qt::AlignHCenter | Qt::AlignVCenter, mTitleLabel); } } if (graphHeight < 1) graphHeight = 1; p.scale(1.0, -1.0); p.drawImage(QRect(0, -height(), width() - mHistoryOffset, graphHeight), mHistoryImage, QRect(mHistoryOffset, 0, width() - mHistoryOffset, 100)); if (mHistoryOffset) p.drawImage(QRect(width() - mHistoryOffset, -height(), mHistoryOffset, graphHeight), mHistoryImage, QRect(0, 0, mHistoryOffset, 100)); p.resetTransform(); p.setRenderHint(QPainter::Antialiasing); p.setPen(mColours.gridColour); qreal w = static_cast(width()); if (hasTitle) p.drawLine(QPointF(0.0, graphTop + 0.5), QPointF(w, graphTop + 0.5)); // 0.5 looks better with antialiasing for (int l = 0; l < mGridLines; ++l) { qreal y = graphTop + static_cast(l + 1) * graphHeight / (static_cast(mGridLines + 1)); p.drawLine(QPointF(0.0, y), QPointF(w, y)); } } void LXQtSysStatContent::toolTipInfo(QString const & tooltip) { setToolTip(QString("%1(%2)
%3") .arg(QCoreApplication::translate("LXQtSysStatConfiguration", mDataType.toStdString().c_str())) .arg(QCoreApplication::translate("LXQtSysStatConfiguration", mDataSource.toStdString().c_str())) .arg(tooltip)); }