2
KSysGuard, the KDE System Guard
4
Copyright (c) 1999 - 2002 Chris Schlaeger <cs@kde.org>
6
This program is free software; you can redistribute it and/or
7
modify it under the terms of the GNU General Public
8
License version 2 or at your option version 3 as published by
9
the Free Software Foundation.
11
This program is distributed in the hope that it will be useful,
12
but WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
GNU General Public License for more details.
16
You should have received a copy of the GNU General Public License
17
along with this program; if not, write to the Free Software
18
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22
#include <QtXml/qdom.h>
23
#include <QtGui/QImage>
24
#include <QtGui/QToolTip>
25
#include <QtGui/QVBoxLayout>
26
#include <QtGui/QHBoxLayout>
27
#include <QtGui/QLabel>
28
#include <QtGui/QFontInfo>
29
#include <QtGui/QResizeEvent>
34
#include <kmessagebox.h>
35
#include <kapplication.h>
36
#include <ksignalplotter.h>
37
#include <kstandarddirs.h>
39
#include <ksgrd/SensorManager.h>
40
#include "StyleEngine.h"
42
#include "FancyPlotterSettings.h"
44
#include "FancyPlotter.h"
52
QString summationName;
55
class FancyPlotterLabel : public QLabel {
57
FancyPlotterLabel(QWidget *parent) : QLabel(parent) {
58
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
60
shortHeadingWidth = 0;
62
setLayoutDirection(Qt::LeftToRight); //We do this because we organise the strings ourselves.. is this going to muck it up though for RTL languages?
64
~FancyPlotterLabel() {
66
void setLabel(const QString &name, const QColor &color) {
69
if(indicatorSymbol.isNull()) {
70
if(fontMetrics().inFont(QChar(0x25CF)))
71
indicatorSymbol = QChar(0x25CF);
73
indicatorSymbol = '#';
78
void setValueText(const QString &value) {
79
//value can have multiple strings, separated with the 0x9c character
80
valueText = value.split(QChar(0x9c));
84
virtual void resizeEvent( QResizeEvent * ) {
85
QFontMetrics fm = fontMetrics();
87
if(valueText.isEmpty()) {
88
if(longHeadingWidth < width())
89
setText(longHeadingText);
91
setText(shortHeadingText);
94
QString value = valueText.first();
96
int textWidth = fm.boundingRect(value).width();
97
if(textWidth + longHeadingWidth < width())
98
setBothText(longHeadingText, value);
99
else if(textWidth + shortHeadingWidth < width())
100
setBothText(shortHeadingText, value);
102
int valueTextCount = valueText.count();
104
for(i = 1; i < valueTextCount; ++i) {
105
textWidth = fm.boundingRect(valueText.at(i)).width();
106
if(textWidth + shortHeadingWidth <= width()) {
110
if(i < valueTextCount)
111
setBothText(shortHeadingText, valueText.at(i));
113
setText(noHeadingText + valueText.last()); //This just sets the color of the text
116
void changeLabel(const QColor &_color) {
119
if ( kapp->layoutDirection() == Qt::RightToLeft )
120
longHeadingText = QString(": ") + labelName + " <font color=\"" + color.name() + "\">" + indicatorSymbol + "</font>";
122
longHeadingText = QString("<qt><font color=\"") + color.name() + "\">" + indicatorSymbol + "</font> " + labelName + " :";
123
shortHeadingText = QString("<qt><font color=\"") + color.name() + "\">" + indicatorSymbol + "</font>";
124
noHeadingText = QString("<qt><font color=\"") + color.name() + "\">";
126
textMargin = fontMetrics().width('x') + margin()*2 + frameWidth()*2;
127
longHeadingWidth = fontMetrics().boundingRect(labelName + " :" + indicatorSymbol + " x").width() + textMargin;
128
shortHeadingWidth = fontMetrics().boundingRect(indicatorSymbol).width() + textMargin;
129
setMinimumWidth(shortHeadingWidth);
133
void setBothText(const QString &heading, const QString & value) {
134
if(QApplication::layoutDirection() == Qt::LeftToRight)
135
setText(heading + ' ' + value);
137
setText("<qt>" + value + ' ' + heading);
140
QString longHeadingText;
141
QString shortHeadingText;
142
QString noHeadingText;
143
int longHeadingWidth;
144
int shortHeadingWidth;
145
QList<QString> valueText;
148
static QChar indicatorSymbol;
151
QChar FancyPlotterLabel::indicatorSymbol;
153
FancyPlotter::FancyPlotter( QWidget* parent,
154
const QString &title,
155
SharedSettings *workSheetSettings)
156
: KSGRD::SensorDisplay( parent, title, workSheetSettings )
160
mSensorReportedMax = mSensorReportedMin = 0;
161
mSensorManualMax = mSensorManualMin = 0;
162
mUseManualRange = false;
164
mLabelsWidget = NULL;
166
//The unicode character 0x25CF is a big filled in circle. We would prefer to use this in the tooltip.
167
//However it's maybe possible that the font used to draw the tooltip won't have it. So we fall back to a
169
QFontMetrics fm(QToolTip::font());
170
if(fm.inFont(QChar(0x25CF)))
171
mIndicatorSymbol = QChar(0x25CF);
173
mIndicatorSymbol = '#';
175
QBoxLayout *layout = new QVBoxLayout(this);
176
layout->setSpacing(0);
177
mPlotter = new KSignalPlotter( this );
178
int axisTextWidth = fontMetrics().width(i18nc("Largest axis title", "99999 XXXX"));
179
mPlotter->setMaxAxisTextWidth( axisTextWidth );
180
mPlotter->setUseAutoRange( true );
181
mHeading = new QLabel(translatedTitle(), this);
183
headingFont.setFamily("Sans Serif");
184
headingFont.setWeight(QFont::Bold);
185
headingFont.setPointSize(11);
186
mHeading->setFont(headingFont);
187
layout->addWidget(mHeading);
188
layout->addWidget(mPlotter);
190
/* Create a set of labels underneath the graph. */
191
mLabelsWidget = new QWidget;
192
layout->addWidget(mLabelsWidget);
193
QBoxLayout *outerLabelLayout = new QHBoxLayout(mLabelsWidget);
194
outerLabelLayout->setSpacing(0);
195
outerLabelLayout->setContentsMargins(0,0,0,0);
197
/* create a spacer to fill up the space up to the start of the graph */
198
outerLabelLayout->addItem(new QSpacerItem(axisTextWidth + 10, 0, QSizePolicy::Preferred));
200
mLabelLayout = new QHBoxLayout;
201
outerLabelLayout->addLayout(mLabelLayout);
202
mLabelLayout->setContentsMargins(0,0,0,0);
204
font.setPointSize( KSGRD::Style->fontSize() );
205
mPlotter->setFont( font );
207
/* All RMB clicks to the mPlotter widget will be handled by
208
* SensorDisplay::eventFilter. */
209
mPlotter->installEventFilter( this );
211
setPlotterWidget( mPlotter );
212
connect(mPlotter, SIGNAL(axisScaleChanged()), this, SLOT(plotterAxisScaleChanged()));
213
QDomElement emptyElement;
214
restoreSettings(emptyElement);
217
FancyPlotter::~FancyPlotter()
221
void FancyPlotter::setTitle( const QString &title ) { //virtual
222
KSGRD::SensorDisplay::setTitle( title );
224
mHeading->setText(translatedTitle());
227
bool FancyPlotter::eventFilter( QObject* object, QEvent* event ) { //virtual
228
if(event->type() == QEvent::ToolTip)
230
return SensorDisplay::eventFilter(object, event);
233
void FancyPlotter::configureSettings()
237
mSettingsDialog = new FancyPlotterSettings( this, mSharedSettings->locked );
239
mSettingsDialog->setTitle( title() );
240
mSettingsDialog->setUseManualRange( mUseManualRange );
241
if(mUseManualRange) {
242
mSettingsDialog->setMinValue( mSensorManualMin );
243
mSettingsDialog->setMaxValue( mSensorManualMax );
245
mSettingsDialog->setMinValue( mSensorReportedMin );
246
mSettingsDialog->setMaxValue( mSensorReportedMax );
249
mSettingsDialog->setHorizontalScale( mPlotter->horizontalScale() );
251
mSettingsDialog->setShowVerticalLines( mPlotter->showVerticalLines() );
252
mSettingsDialog->setVerticalLinesDistance( mPlotter->verticalLinesDistance() );
253
mSettingsDialog->setVerticalLinesScroll( mPlotter->verticalLinesScroll() );
255
mSettingsDialog->setShowHorizontalLines( mPlotter->showHorizontalLines() );
257
mSettingsDialog->setShowAxis( mPlotter->showAxis() );
259
mSettingsDialog->setFontSize( mPlotter->font().pointSize() );
261
mSettingsDialog->setRangeUnits( mUnit );
262
mSettingsDialog->setRangeUnits( mUnit );
264
mSettingsDialog->setStackBeams( mPlotter->stackGraph() );
266
bool hasIntegerRange = true;
267
SensorModelEntry::List list;
268
for ( int i = 0; i < (int)mBeams; ++i ) {
269
FPSensorProperties *sensor = NULL;
270
//find the first sensor for this beam, since one beam can have many sensors
271
for ( int j = 0; j < sensors().count(); ++j ) {
272
FPSensorProperties *sensor2 = static_cast<FPSensorProperties *>(sensors().at(j));
273
if(sensor2->beamId == i)
278
SensorModelEntry entry;
280
entry.setHostName( sensor->hostName() );
281
entry.setSensorName( sensor->regExpName().isEmpty()?sensor->name():sensor->regExpName() );
282
entry.setUnit( sensor->unit() );
283
entry.setStatus( sensor->isOk() ? i18n( "OK" ) : i18n( "Error" ) );
284
entry.setColor( mPlotter->beamColor( i ) );
285
if(!sensor->isInteger)
286
hasIntegerRange = false;
287
list.append( entry );
289
mSettingsDialog->setSensors( list );
290
mSettingsDialog->setHasIntegerRange( hasIntegerRange );
292
connect( mSettingsDialog, SIGNAL( applyClicked() ), this, SLOT( applySettings() ) );
293
connect( mSettingsDialog, SIGNAL( okClicked() ), this, SLOT( applySettings() ) );
294
connect( mSettingsDialog, SIGNAL( finished() ), this, SLOT( settingsFinished() ) );
296
mSettingsDialog->show();
299
void FancyPlotter::settingsFinished()
301
mSettingsDialog->delayedDestruct();
305
void FancyPlotter::applySettings() {
306
setTitle( mSettingsDialog->title() );
308
mUseManualRange = mSettingsDialog->useManualRange();
309
if(mUseManualRange) {
310
mSensorManualMin = mSettingsDialog->minValue();
311
mSensorManualMax = mSettingsDialog->maxValue();
312
mPlotter->changeRange( mSettingsDialog->minValue(), mSettingsDialog->maxValue() );
315
mPlotter->changeRange( mSensorReportedMin, mSensorReportedMax );
318
if ( mPlotter->horizontalScale() != mSettingsDialog->horizontalScale() ) {
319
mPlotter->setHorizontalScale( mSettingsDialog->horizontalScale() );
322
mPlotter->setShowVerticalLines( mSettingsDialog->showVerticalLines() );
323
mPlotter->setVerticalLinesDistance( mSettingsDialog->verticalLinesDistance() );
324
mPlotter->setVerticalLinesScroll( mSettingsDialog->verticalLinesScroll() );
326
mPlotter->setShowHorizontalLines( mSettingsDialog->showHorizontalLines() );
328
mPlotter->setShowAxis( mSettingsDialog->showAxis() );
329
mPlotter->setStackGraph( mSettingsDialog->stackBeams() );
332
font.setPointSize( mSettingsDialog->fontSize() );
333
mPlotter->setFont( font );
335
QList<int> deletedBeams = mSettingsDialog->deleted();
336
for ( int i =0; i < deletedBeams.count(); ++i) {
337
removeBeam(deletedBeams[i]);
339
mSettingsDialog->clearDeleted(); //We have deleted them, so clear the deleted
341
reorderBeams(mSettingsDialog->order());
342
mSettingsDialog->resetOrder(); //We have now reordered the sensors, so reset the order
344
SensorModelEntry::List list = mSettingsDialog->sensors();
346
for( int i = 0; i < list.count(); i++)
347
setBeamColor(i, list[i].color());
351
void FancyPlotter::resizeEvent( QResizeEvent* )
353
bool showHeading = true;;
354
bool showLabels = true;;
356
if( height() < mLabelsWidget->sizeHint().height() + mHeading->sizeHint().height() + mPlotter->minimumHeight() )
358
if( height() < mLabelsWidget->sizeHint().height() + mPlotter->minimumHeight() )
360
mHeading->setVisible(showHeading);
361
mLabelsWidget->setVisible(showLabels);
365
void FancyPlotter::reorderBeams(const QList<int> & orderOfBeams)
367
//Q_ASSERT(orderOfBeams.size() == mLabelLayout.size()); Commented out because it cause compile problems in some cases??
369
mPlotter->reorderBeams(orderOfBeams);
370
//Reorder the labels underneath the graph
371
QList<QLayoutItem *> labelsInOldOrder;
372
while(!mLabelLayout->isEmpty())
373
labelsInOldOrder.append(mLabelLayout->takeAt(0));
375
for(int newIndex = 0; newIndex < orderOfBeams.count(); newIndex++) {
376
int oldIndex = orderOfBeams.at(newIndex);
377
mLabelLayout->addItem(labelsInOldOrder.at(oldIndex));
380
for ( int i = 0; i < sensors().count(); ++i ) {
381
FPSensorProperties *sensor = static_cast<FPSensorProperties *>(sensors().at(i));
382
for(int newIndex = 0; newIndex < orderOfBeams.count(); newIndex++) {
383
int oldIndex = orderOfBeams.at(newIndex);
384
if(oldIndex == sensor->beamId) {
385
sensor->beamId = newIndex;
391
void FancyPlotter::applyStyle()
393
QFont font = mPlotter->font();
394
font.setPointSize(KSGRD::Style->fontSize() );
395
mPlotter->setFont( font );
396
for ( int i = 0; i < mPlotter->numBeams() &&
397
(unsigned int)i < KSGRD::Style->numSensorColors(); ++i ) {
398
setBeamColor(i, KSGRD::Style->sensorColor(i));
403
void FancyPlotter::setBeamColor(int i, const QColor &color)
405
mPlotter->setBeamColor( i, color );
406
static_cast<FancyPlotterLabel *>((static_cast<QWidgetItem *>(mLabelLayout->itemAt(i)))->widget())->changeLabel(color);
408
bool FancyPlotter::addSensor( const QString &hostName, const QString &name,
409
const QString &type, const QString &title )
411
return addSensor( hostName, name, type, title,
412
KSGRD::Style->sensorColor( mBeams ), QString(), mBeams );
415
bool FancyPlotter::addSensor( const QString &hostName, const QString &name,
416
const QString &type, const QString &title,
417
const QColor &color, const QString ®expName,
418
int beamId, const QString & summationName)
420
if ( type != "integer" && type != "float" )
424
registerSensor( new FPSensorProperties( hostName, name, type, title, color, regexpName, beamId, summationName ) );
426
/* To differentiate between answers from value requests and info
427
* requests we add 100 to the beam index for info requests. */
428
sendRequest( hostName, name + '?', sensors().size() - 1 + 100 );
430
if((int)mBeams == beamId) {
431
mPlotter->addBeam( color );
432
/* Add a label for this beam */
433
FancyPlotterLabel *label = new FancyPlotterLabel(this);
434
mLabelLayout->addWidget(label);
435
if(!summationName.isEmpty()) {
436
label->setLabel(summationName, mPlotter->beamColor(mBeams));
444
bool FancyPlotter::removeBeam( uint beamId )
446
if ( beamId >= mBeams ) {
447
kDebug(1215) << "FancyPlotter::removeBeam: beamId out of range ("
448
<< beamId << ")" << endl;
452
mPlotter->removeBeam( beamId );
454
QWidget *label = (static_cast<QWidgetItem *>(mLabelLayout->takeAt( beamId )))->widget();
455
mLabelLayout->removeWidget(label);
458
mSensorReportedMax = 0;
459
mSensorReportedMin = 0;
460
for ( int i = sensors().count()-1; i >= 0; --i ) {
461
FPSensorProperties *sensor = static_cast<FPSensorProperties *>(sensors().at(i));
463
if(sensor->beamId == (int)beamId)
466
if(sensor->beamId > (int)beamId)
467
sensor->beamId--; //sensor pointer is no longer valid after removing the sensor
468
mSensorReportedMax = qMax(mSensorReportedMax, sensor->maxValue);
469
mSensorReportedMin = qMin(mSensorReportedMin, sensor->minValue);
472
//change the plotter's range to the new maximum
473
if ( !mUseManualRange )
474
mPlotter->changeRange( mSensorReportedMin, mSensorReportedMax );
476
mPlotter->changeRange( mSensorManualMin, mSensorManualMax );
478
//loop through the new sensors to find the new unit
479
for ( int i = 0; i < sensors().count(); i++ ) {
480
FPSensorProperties *sensor = static_cast<FPSensorProperties *>(sensors().at(i));
482
mUnit = sensor->unit();
483
else if(mUnit != sensor->unit()) {
488
//adjust the scale to take into account the removed sensor
489
plotterAxisScaleChanged();
494
void FancyPlotter::setTooltip()
496
QString tooltip = "<qt><p style='white-space:pre'>";
500
bool neednewline = false;
501
bool showingSummationGroup = false;
503
//Note that the number of beams can be less than the number of sensors, since some sensors
504
//get added together for a beam.
505
//For the tooltip, we show all the sensors
506
for ( int i = 0; i < sensors().count(); ++i ) {
507
FPSensorProperties *sensor = static_cast<FPSensorProperties *>(sensors().at(i));
508
description = sensor->description();
509
if(description.isEmpty())
510
description = sensor->name();
513
lastValue = KGlobal::locale()->formatNumber( sensor->lastValue, (sensor->isInteger)?0:-1 );
514
if (sensor->unit() == "%")
515
lastValue = i18nc("units", "%1%", lastValue);
516
else if( !sensor->unit().isEmpty() )
517
lastValue = i18nc("units", QString("%1 ").arg(sensor->unit()).toUtf8(), lastValue);
519
lastValue = i18n("Error");
521
if (beamId != sensor->beamId) {
522
if (!sensor->summationName.isEmpty()) {
523
tooltip += i18nc("%1 is what is being shown statistics for, like 'Memory', 'Swap', etc.", "<p><b>%1:</b><br>", sensor->summationName);
524
showingSummationGroup = true;
526
} else if (showingSummationGroup) {
527
//When a summation group has finished, seperate the next sensor with a newline
528
showingSummationGroup = false;
533
beamId = sensor->beamId;
535
if(sensor->isLocalhost()) {
536
tooltip += QString( "%1%2 %3 (%4)" ).arg( neednewline ? "<br>" : "")
537
.arg("<font color=\"" + mPlotter->beamColor( beamId ).name() + "\">"+mIndicatorSymbol+"</font>")
538
.arg( i18n(description.toUtf8()) )
542
tooltip += QString( "%1%2 %3:%4 (%5)" ).arg( neednewline ? "<br>" : "" )
543
.arg("<font color=\"" + mPlotter->beamColor( beamId ).name() + "\">"+mIndicatorSymbol+"</font>")
544
.arg( sensor->hostName() )
545
.arg( i18n(description.toUtf8()) )
550
// tooltip += "</td></tr></table>";
551
mPlotter->setToolTip( tooltip );
554
void FancyPlotter::sendDataToPlotter( )
556
if(!mSampleBuf.isEmpty() && mBeams != 0) {
557
if((uint)mSampleBuf.count() > mBeams) {
559
return; //ignore invalid results - can happen if a sensor is deleted
561
while((uint)mSampleBuf.count() < mBeams)
562
mSampleBuf.append(mPlotter->lastValue(mSampleBuf.count())); //we might have sensors missing so set their values to the previously known value
563
mPlotter->addSample( mSampleBuf );
565
if(QToolTip::isVisible() && (qApp->topLevelAt(QCursor::pos()) == window()) && mPlotter->geometry().contains(mPlotter->mapFromGlobal( QCursor::pos() ))) {
567
QToolTip::showText(QCursor::pos(), mPlotter->toolTip(), mPlotter);
571
for ( int i = 0; i < sensors().size(); ++i ) {
572
FPSensorProperties *sensor = static_cast<FPSensorProperties *>(sensors().at(i));
573
if(sensor->beamId == beamId)
575
beamId = sensor->beamId;
576
if(sensor->isOk() && mPlotter->numBeams() > beamId) {
579
if(sensor->unit() == mUnit) {
580
precision = (sensor->isInteger && mPlotter->scaleDownBy() == 1)?0:-1;
581
lastValue = mPlotter->lastValueAsString(beamId, precision);
583
precision = (sensor->isInteger)?0:-1;
584
lastValue = KGlobal::locale()->formatNumber( mPlotter->lastValue(beamId), precision );
585
if (sensor->unit() == "%")
586
lastValue = i18nc("units", "%1%", lastValue);
587
else if( !sensor->unit().isEmpty() ) {
588
lastValue = i18nc("units", QString("%1 " + sensor->unit()).toUtf8(), lastValue);
592
if(sensor->maxValue != 0 && sensor->unit() != "%") {
593
//Use a multi length string incase we do not have enough room
594
lastValue = i18n("%1 of %2" "\xc2\x9c" "%1", lastValue, mPlotter->valueAsString(sensor->maxValue, precision) );
597
lastValue = i18n("Error");
599
static_cast<FancyPlotterLabel *>((static_cast<QWidgetItem *>(mLabelLayout->itemAt(beamId)))->widget())->setValueText(lastValue);
606
void FancyPlotter::timerTick() //virtual
608
if(mNumAnswers < sensors().count())
609
sendDataToPlotter(); //we haven't received enough answers yet, but plot what we do have
611
SensorDisplay::timerTick();
613
void FancyPlotter::plotterAxisScaleChanged()
615
//Prevent this being called recursively
616
disconnect(mPlotter, SIGNAL(axisScaleChanged()), this, SLOT(plotterAxisScaleChanged()));
617
KLocalizedString unit;
618
double value = mPlotter->currentMaximumRangeValue();
620
if(value >= 1024*1024*1024*0.7) { //If it's over 0.7TiB, then set the scale to terabytes
621
mPlotter->setScaleDownBy(1024*1024*1024);
622
unit = ki18nc("units", "%1 TiB"); // the unit - terabytes
623
} else if(value >= 1024*1024*0.7) { //If it's over 0.7GiB, then set the scale to gigabytes
624
mPlotter->setScaleDownBy(1024*1024);
625
unit = ki18nc("units", "%1 GiB"); // the unit - gigabytes
626
} else if(value > 1024) {
627
mPlotter->setScaleDownBy(1024);
628
unit = ki18nc("units", "%1 MiB"); // the unit - megabytes
630
mPlotter->setScaleDownBy(1);
631
unit = ki18nc("units", "%1 KiB"); // the unit - kilobytes
633
} else if(mUnit == "KiB/s") {
634
if(value >= 1024*1024*1024*0.7) { //If it's over 0.7TiB, then set the scale to terabytes
635
mPlotter->setScaleDownBy(1024*1024*1024);
636
unit = ki18nc("units", "%1 TiB/s"); // the unit - terabytes per second
637
} else if(value >= 1024*1024*0.7) { //If it's over 0.7GiB, then set the scale to gigabytes
638
mPlotter->setScaleDownBy(1024*1024);
639
unit = ki18nc("units", "%1 GiB/s"); // the unit - gigabytes per second
640
} else if(value > 1024) {
641
mPlotter->setScaleDownBy(1024);
642
unit = ki18nc("units", "%1 MiB/s"); // the unit - megabytes per second
644
mPlotter->setScaleDownBy(1);
645
unit = ki18nc("units", "%1 KiB/s"); // the unit - kilobytes per second
647
} else if(mUnit == "%") {
648
mPlotter->setScaleDownBy(1);
649
unit = ki18nc("units", "%1%"); //the unit - percentage
650
} else if(mUnit.isEmpty()) {
651
unit = ki18nc("unitless - just a number", "%1");
653
#if 0 // the strings are here purely for translation
654
NOOP_I18NC("units", "%1 1/s"); // the unit - 1 per second
655
NOOP_I18NC("units", "%1 s"); // the unit - seconds
656
NOOP_I18NC("units", "%1 MHz"); // the unit - frequency megahertz
658
mPlotter->setScaleDownBy(1);
659
//translate any others
660
unit = ki18nc("units", QString("%1 " + mUnit).toUtf8());
662
mPlotter->setUnit(unit);
664
connect(mPlotter, SIGNAL(axisScaleChanged()), this, SLOT(plotterAxisScaleChanged()));
666
void FancyPlotter::answerReceived( int id, const QList<QByteArray> &answerlist )
670
if(!answerlist.isEmpty()) answer = answerlist[0];
671
if ( (uint)id < 100 ) {
672
//Make sure that we put the answer in the correct place. Its index in the list should be equal to the sensor index. This in turn will contain the beamId
673
if(id >= sensors().count())
674
return; //just ignore if we get a result for an invalid sensor
675
FPSensorProperties *sensor = static_cast<FPSensorProperties *>(sensors().at(id));
676
int beamId = sensor->beamId;
677
double value = answer.toDouble();
678
while(beamId > mSampleBuf.count())
679
mSampleBuf.append(0); //we might have sensors missing so set their values to zero
681
if(beamId == mSampleBuf.count()) {
682
mSampleBuf.append( value );
684
mSampleBuf[beamId] += value; //If we get two answers for the same beamid, we should add them together. That's how the summation works
686
sensor->lastValue = value;
687
/* We received something, so the sensor is probably ok. */
688
sensorError( id, false );
690
if(++mNumAnswers == sensors().count())
691
sendDataToPlotter(); //we have received all the answers so start plotting
692
} else if ( id >= 100 && id < 200 ) {
693
if( (id - 100) >= sensors().count())
694
return; //just ignore if we get a result for an invalid sensor
695
KSGRD::SensorFloatInfo info( answer );
696
QString unit = info.unit();
697
if(unit.toUpper() == "KB" || unit.toUpper() == "KIB")
699
if(unit.toUpper() == "KB/S" || unit.toUpper() == "KIB/S")
702
if(id == 100) //if we are the first sensor, just use that sensors units as the global unit
704
else if(unit != mUnit)
705
mUnit = ""; //if the units don't match, then set the units on the scale to empty, to avoid any confusion
707
mSensorReportedMax = qMax(mSensorReportedMax, info.max());
708
mSensorReportedMin = qMin(mSensorReportedMin, info.min());
710
if ( !mUseManualRange )
711
mPlotter->changeRange( mSensorReportedMin, mSensorReportedMax );
712
plotterAxisScaleChanged();
714
FPSensorProperties *sensor = static_cast<FPSensorProperties *>(sensors().at(id - 100));
715
sensor->maxValue = info.max();
716
sensor->minValue = info.min();
717
sensor->setUnit( unit );
718
sensor->setDescription( info.name() );
720
QString summationName = sensor->summationName;
721
int beamId = sensor->beamId;
723
Q_ASSERT(beamId < mPlotter->numBeams());
724
Q_ASSERT(beamId < mLabelLayout->count());
726
if(summationName.isEmpty())
727
static_cast<FancyPlotterLabel *>((static_cast<QWidgetItem *>(mLabelLayout->itemAt(beamId)))->widget())->setLabel(info.name(), mPlotter->beamColor(beamId));
729
} else if( id == 200) {
730
/* FIXME This doesn't check the host! */
731
if(!mSensorsToAdd.isEmpty()) {
732
foreach(SensorToAdd *sensor, mSensorsToAdd) {
733
int beamId = mBeams; //Assign the next sensor to the next available beamId
734
for ( int i = 0; i < answerlist.count(); ++i ) {
735
if ( answerlist[ i ].isEmpty() )
737
QString sensorName = QString::fromUtf8(answerlist[ i ].split('\t')[0]);
738
if(sensor->name.exactMatch(sensorName)) {
739
if(sensor->summationName.isEmpty())
740
beamId = mBeams; //If summationName is not empty then reuse the previous beamId. In this way we can have multiple sensors with the same beamId, which can then be summed together
742
if(!sensor->colors.isEmpty() )
743
color = sensor->colors.takeFirst();
744
else if(KSGRD::Style->numSensorColors() != 0)
745
color = KSGRD::Style->sensorColor( beamId % KSGRD::Style->numSensorColors());
746
addSensor( sensor->hostname, sensorName,
747
(sensor->type.isEmpty()) ? "float" : sensor->type
748
, "", color, sensor->name.pattern(), beamId, sensor->summationName);
752
qDeleteAll(mSensorsToAdd);
753
mSensorsToAdd.clear();
758
bool FancyPlotter::restoreSettings( QDomElement &element )
760
mUseManualRange = element.attribute( "manualRange", "0" ).toInt();
762
if(mUseManualRange) {
763
mSensorManualMax = element.attribute( "max" ).toDouble();
764
mSensorManualMin = element.attribute( "min" ).toDouble();
765
mPlotter->changeRange( mSensorManualMin, mSensorManualMax );
767
mPlotter->changeRange( mSensorReportedMin, mSensorReportedMax );
770
mPlotter->setUseAutoRange(element.attribute( "autoRange", "1" ).toInt());
772
// Do not restore the color settings from a previous version
773
int version = element.attribute("version", "0").toInt();
775
mPlotter->setShowVerticalLines( element.attribute( "vLines", "0" ).toUInt() );
776
mPlotter->setVerticalLinesDistance( element.attribute( "vDistance", "30" ).toUInt() );
777
mPlotter->setVerticalLinesScroll( element.attribute( "vScroll", "0" ).toUInt() );
778
mPlotter->setHorizontalScale( element.attribute( "hScale", "6" ).toUInt() );
780
mPlotter->setShowHorizontalLines( element.attribute( "hLines", "1" ).toUInt() );
781
mPlotter->setStackGraph( element.attribute("stacked", "0").toInt());
783
QString filename = element.attribute( "svgBackground");
784
if (!filename.isEmpty() && filename[0] == '/') {
785
KStandardDirs* kstd = KGlobal::dirs();
786
filename = kstd->findResource( "data", "ksysguard/" + filename);
788
mPlotter->setSvgBackground( filename );
790
mPlotter->setShowAxis( element.attribute( "labels", "1" ).toUInt() );
791
uint fontsize = element.attribute( "fontSize", "0").toUInt();
792
if(fontsize == 0) fontsize = KSGRD::Style->fontSize();
794
font.setPointSize( fontsize );
795
mPlotter->setFont( font );
797
QDomNodeList dnList = element.elementsByTagName( "beam" );
798
for ( int i = 0; i < dnList.count(); ++i ) {
799
QDomElement el = dnList.item( i ).toElement();
800
if(el.hasAttribute("regexpSensorName")) {
801
SensorToAdd *sensor = new SensorToAdd();
802
sensor->name = QRegExp(el.attribute("regexpSensorName"));
803
sensor->hostname = el.attribute( "hostName" );
804
sensor->type = el.attribute( "sensorType" );
805
sensor->summationName = el.attribute("summationName");
806
QStringList colors = el.attribute("color").split(',');
808
foreach(const QString &color, colors) {
809
int c = color.toUInt( &ok, 0 );
811
QColor col( (c & 0xff0000) >> 16, (c & 0xff00) >> 8, (c & 0xff), (c & 0xff000000) >> 24);
813
if(col.alpha() == 0) col.setAlpha(255);
814
sensor->colors << col;
817
sensor->colors << KSGRD::Style->sensorColor( i );
820
sensor->colors << KSGRD::Style->sensorColor( i );
822
mSensorsToAdd.append(sensor);
823
sendRequest( sensor->hostname, "monitors", 200 );
825
addSensor( el.attribute( "hostName" ), el.attribute( "sensorName" ),
826
( el.attribute( "sensorType" ).isEmpty() ? "float" :
827
el.attribute( "sensorType" ) ), "", restoreColor( el, "color",
828
KSGRD::Style->sensorColor( i ) ), QString(), mBeams, el.attribute("summationName") );
831
SensorDisplay::restoreSettings( element );
836
bool FancyPlotter::saveSettings( QDomDocument &doc, QDomElement &element)
838
element.setAttribute( "autoRange", mPlotter->useAutoRange() );
840
element.setAttribute( "manualRange", mUseManualRange );
841
if(mUseManualRange) {
842
element.setAttribute( "min", mSensorManualMin );
843
element.setAttribute( "max", mSensorManualMax );
847
element.setAttribute( "vLines", mPlotter->showVerticalLines() );
848
element.setAttribute( "vDistance", mPlotter->verticalLinesDistance() );
849
element.setAttribute( "vScroll", mPlotter->verticalLinesScroll() );
851
element.setAttribute( "hScale", mPlotter->horizontalScale() );
853
element.setAttribute( "hLines", mPlotter->showHorizontalLines() );
855
element.setAttribute( "svgBackground", mPlotter->svgBackground() );
856
element.setAttribute( "stacked", mPlotter->stackGraph() );
858
element.setAttribute( "version", 1 );
859
element.setAttribute( "labels", mPlotter->showAxis() );
861
QHash<QString,QDomElement> hash;
863
for ( int i = 0; i < sensors().size(); ++i ) {
864
FPSensorProperties *sensor = static_cast<FPSensorProperties *>(sensors().at(i));
865
if(sensor->beamId == beamId)
867
beamId = sensor->beamId;
869
QString regExpName = sensor->regExpName();
870
if(!regExpName.isEmpty() && hash.contains( regExpName )) {
871
QDomElement oldBeam = hash.value(regExpName);
872
saveColorAppend( oldBeam, "color", mPlotter->beamColor( beamId ) );
874
QDomElement beam = doc.createElement( "beam" );
875
element.appendChild( beam );
876
beam.setAttribute( "hostName", sensor->hostName() );
877
if(regExpName.isEmpty())
878
beam.setAttribute( "sensorName", sensor->name() );
880
beam.setAttribute( "regexpSensorName", sensor->regExpName() );
881
hash[regExpName] = beam;
883
if(!sensor->summationName.isEmpty())
884
beam.setAttribute( "summationName", sensor->summationName);
885
beam.setAttribute( "sensorType", sensor->type() );
886
saveColor( beam, "color", mPlotter->beamColor( beamId ) );
889
SensorDisplay::saveSettings( doc, element );
894
bool FancyPlotter::hasSettingsDialog() const
899
FPSensorProperties::FPSensorProperties()
903
FPSensorProperties::FPSensorProperties( const QString &hostName,
906
const QString &description,
908
const QString ®expName,
910
const QString &summationName_ )
911
: KSGRD::SensorProperties( hostName, name, type, description),
914
setRegExpName(regexpName);
916
summationName = summationName_;
920
isInteger = (type == "integer");
923
FPSensorProperties::~FPSensorProperties()
927
void FPSensorProperties::setColor( const QColor &color )
932
QColor FPSensorProperties::color() const
938
#include "FancyPlotter.moc"