Added a signalplotter to plasma.
svn path=/trunk/KDE/kdebase/workspace/libs/plasma/; revision=699576
This commit is contained in:
parent
482f9824ba
commit
9268590942
@ -45,6 +45,7 @@ set(plasma_LIB_SRCS
|
||||
# widgets/rectangle.cpp
|
||||
widgets/widget.cpp
|
||||
widgets/vboxlayout.cpp
|
||||
widgets/signalplotter.cpp
|
||||
)
|
||||
|
||||
if(QT_QTOPENGL_FOUND AND OPENGL_FOUND)
|
||||
@ -114,6 +115,7 @@ install(FILES
|
||||
# widgets/rectangle.h
|
||||
widgets/vboxlayout.h
|
||||
widgets/widget.h
|
||||
widgets/signalplotter.h
|
||||
DESTINATION ${INCLUDE_INSTALL_DIR}/plasma/widgets)
|
||||
|
||||
install(FILES
|
||||
|
971
widgets/signalplotter.cpp
Normal file
971
widgets/signalplotter.cpp
Normal file
@ -0,0 +1,971 @@
|
||||
/*
|
||||
* KSysGuard, the KDE System Guard
|
||||
*
|
||||
* Copyright 1999 - 2002 Chris Schlaeger <cs@kde.org>
|
||||
* Copyright 2006 John Tapsell <tapsell@kde.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Library General Public License version 2 as
|
||||
* published by the Free Software Foundation
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <QList>
|
||||
#include <QPalette>
|
||||
#include <QtGui/QPainter>
|
||||
#include <QtGui/QPixmap>
|
||||
#include <QtGui/QPainterPath>
|
||||
#include <QtGui/QPolygon>
|
||||
|
||||
#include <KDebug>
|
||||
#include <KGlobal>
|
||||
#include <KLocale>
|
||||
#include <KApplication>
|
||||
#include <KStandardDirs>
|
||||
|
||||
#include <plasma/svg.h>
|
||||
|
||||
#include "signalplotter.h"
|
||||
|
||||
namespace Plasma
|
||||
{
|
||||
|
||||
class SignalPlotter::Private
|
||||
{
|
||||
public:
|
||||
Private()
|
||||
: svgBackground(0)
|
||||
{ }
|
||||
~Private() { }
|
||||
|
||||
int precision;
|
||||
uint samples;
|
||||
uint bezierCurveOffset;
|
||||
|
||||
double scaledBy;
|
||||
double verticalMin;
|
||||
double verticalMax;
|
||||
double niceVertMin;
|
||||
double niceVertMax;
|
||||
double niceVertRange;
|
||||
|
||||
bool fillPlots;
|
||||
bool showLabels;
|
||||
bool showTopBar;
|
||||
bool stackPlots;
|
||||
bool useAutoRange;
|
||||
bool showThinFrame;
|
||||
|
||||
bool showVerticalLines;
|
||||
bool verticalLinesScroll;
|
||||
uint verticalLinesOffset;
|
||||
uint verticalLinesDistance;
|
||||
QColor verticalLinesColor;
|
||||
|
||||
bool showHorizontalLines;
|
||||
uint horizontalScale;
|
||||
uint horizontalLinesCount;
|
||||
QColor horizontalLinesColor;
|
||||
|
||||
Svg *svgBackground;
|
||||
QString svgFilename;
|
||||
|
||||
QColor fontColor;
|
||||
QColor backgroundColor;
|
||||
QPixmap backgroundPixmap;
|
||||
|
||||
QFont font;
|
||||
QString title;
|
||||
QString unit;
|
||||
|
||||
QList<PlotColor> plotColors;
|
||||
QList<QList<double> > plotData;
|
||||
};
|
||||
|
||||
SignalPlotter::SignalPlotter(Widget *parent)
|
||||
: Widget(parent),
|
||||
d(new Private)
|
||||
{
|
||||
d->precision = 0;
|
||||
d->bezierCurveOffset = 0;
|
||||
d->samples = 0;
|
||||
d->verticalMin = d->verticalMax = 0.0;
|
||||
d->niceVertMin = d->niceVertMax = 0.0;
|
||||
d->niceVertRange = 0;
|
||||
d->useAutoRange = true;
|
||||
d->scaledBy = 1;
|
||||
d->showThinFrame = true;
|
||||
|
||||
// Anything smaller than this does not make sense.
|
||||
setMinimumSize(QSizeF(16, 16));
|
||||
|
||||
d->showVerticalLines = true;
|
||||
d->verticalLinesColor = QColor("black");
|
||||
d->verticalLinesDistance = 30;
|
||||
d->verticalLinesScroll = true;
|
||||
d->verticalLinesOffset = 0;
|
||||
d->horizontalScale = 1;
|
||||
|
||||
d->showHorizontalLines = true;
|
||||
d->horizontalLinesColor = QColor("black");
|
||||
d->horizontalLinesCount = 5;
|
||||
|
||||
d->showLabels = true;
|
||||
d->showTopBar = true;
|
||||
d->stackPlots = true;
|
||||
d->fillPlots = true;
|
||||
|
||||
d->svgBackground = 0;
|
||||
d->backgroundColor = QColor(0,0,0);
|
||||
}
|
||||
|
||||
SignalPlotter::~SignalPlotter()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
Qt::Orientations SignalPlotter::expandingDirections() const
|
||||
{
|
||||
return Qt::Horizontal | Qt::Vertical;
|
||||
}
|
||||
|
||||
QString SignalPlotter::unit() const
|
||||
{
|
||||
return d->unit;
|
||||
}
|
||||
void SignalPlotter::setUnit(const QString &unit)
|
||||
{
|
||||
d->unit= unit;
|
||||
}
|
||||
|
||||
void SignalPlotter::addPlot(const QColor &color)
|
||||
{
|
||||
// When we add a new plot, go back and set the data for this plot to 0 for
|
||||
// all the other times. This is because it makes it easier for moveSensors.
|
||||
foreach (QList<double> data, d->plotData) {
|
||||
data.append(0);
|
||||
}
|
||||
PlotColor newColor;
|
||||
newColor.color = color;
|
||||
newColor.darkColor = color.dark(150);
|
||||
d->plotColors.append(newColor);
|
||||
}
|
||||
|
||||
void SignalPlotter::addSample(const QList<double>& sampleBuf)
|
||||
{
|
||||
if (d->samples < 4) {
|
||||
// It might be possible, under some race conditions, for addSample
|
||||
// to be called before d->samples is set. This is just to be safe.
|
||||
kDebug(1215) << "Error - d->samples is only " << d->samples << endl;
|
||||
updateDataBuffers();
|
||||
kDebug(1215) << "d->samples is now " << d->samples << endl;
|
||||
if (d->samples < 4)
|
||||
return;
|
||||
}
|
||||
d->plotData.prepend(sampleBuf);
|
||||
Q_ASSERT(sampleBuf.count() == d->plotColors.count());
|
||||
if ((uint)d->plotData.size() > d->samples) {
|
||||
d->plotData.removeLast(); // we have too many. Remove the last item
|
||||
if ((uint)d->plotData.size() > d->samples)
|
||||
d->plotData.removeLast(); // If we still have too many, then we have resized the widget. Remove one more. That way we will slowly resize to the new size
|
||||
}
|
||||
|
||||
if (d->bezierCurveOffset >= 2) d->bezierCurveOffset = 0;
|
||||
else d->bezierCurveOffset++;
|
||||
|
||||
Q_ASSERT((uint)d->plotData.size() >= d->bezierCurveOffset);
|
||||
|
||||
// If the vertical lines are scrolling, increment the offset
|
||||
// so they move with the data.
|
||||
if (d->verticalLinesScroll) {
|
||||
d->verticalLinesOffset = (d->verticalLinesOffset + d->horizontalScale)
|
||||
% d->verticalLinesDistance;
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
void SignalPlotter::reorderPlots(const QList<uint>& newOrder)
|
||||
{
|
||||
if (newOrder.count() != d->plotColors.count()) {
|
||||
kDebug(1215) << "neworder has " << newOrder.count() << " and plot colors is " << d->plotColors.count() << endl;
|
||||
return;
|
||||
}
|
||||
foreach (QList<double> data, d->plotData) {
|
||||
if (newOrder.count() != data.count()) {
|
||||
kDebug(1215) << "Serious problem in move sample. plotdata[i] has " << data.count() << " and neworder has " << newOrder.count() << endl;
|
||||
} else {
|
||||
QList<double> newPlot;
|
||||
for (int i = 0; i < newOrder.count(); i++) {
|
||||
int newIndex = newOrder[i];
|
||||
newPlot.append(data.at(newIndex));
|
||||
}
|
||||
data = newPlot;
|
||||
}
|
||||
}
|
||||
QList<PlotColor> newPlotColors;
|
||||
for (int i = 0; i < newOrder.count(); i++) {
|
||||
int newIndex = newOrder[i];
|
||||
PlotColor newColor = d->plotColors.at(newIndex);
|
||||
newPlotColors.append(newColor);
|
||||
}
|
||||
d->plotColors = newPlotColors;
|
||||
}
|
||||
|
||||
void SignalPlotter::setVerticalRange(double min, double max)
|
||||
{
|
||||
d->verticalMin = min;
|
||||
d->verticalMax = max;
|
||||
calculateNiceRange();
|
||||
}
|
||||
|
||||
QList<PlotColor> &SignalPlotter::plotColors()
|
||||
{
|
||||
return d->plotColors;
|
||||
}
|
||||
|
||||
void SignalPlotter::removePlot(uint pos)
|
||||
{
|
||||
if (pos >= (uint)d->plotColors.size()) return;
|
||||
d->plotColors.removeAt(pos);
|
||||
|
||||
foreach (QList<double> data, d->plotData) {
|
||||
if ((uint)data.size() >= pos)
|
||||
data.removeAt(pos);
|
||||
}
|
||||
}
|
||||
|
||||
void SignalPlotter::scale(qreal delta)
|
||||
{
|
||||
if (d->scaledBy == delta) return;
|
||||
d->scaledBy = delta;
|
||||
d->backgroundPixmap = QPixmap(); // we changed a paint setting, so reset the cache
|
||||
calculateNiceRange();
|
||||
}
|
||||
|
||||
double SignalPlotter::scaledBy() const
|
||||
{
|
||||
return d->scaledBy;
|
||||
}
|
||||
|
||||
void SignalPlotter::setTitle(const QString &title)
|
||||
{
|
||||
if (d->title == title) return;
|
||||
d->title = title;
|
||||
d->backgroundPixmap = QPixmap(); // we changed a paint setting, so reset the cache
|
||||
}
|
||||
|
||||
QString SignalPlotter::title() const
|
||||
{
|
||||
return d->title;
|
||||
}
|
||||
|
||||
void SignalPlotter::setUseAutoRange(bool value)
|
||||
{
|
||||
d->useAutoRange = value;
|
||||
calculateNiceRange();
|
||||
// this change will be detected in paint and the image cache regenerated
|
||||
}
|
||||
|
||||
bool SignalPlotter::useAutoRange() const
|
||||
{
|
||||
return d->useAutoRange;
|
||||
}
|
||||
|
||||
double SignalPlotter::verticalMinValue() const
|
||||
{
|
||||
return d->verticalMin;
|
||||
}
|
||||
|
||||
double SignalPlotter::verticalMaxValue() const
|
||||
{
|
||||
return d->verticalMax;
|
||||
}
|
||||
|
||||
void SignalPlotter::setHorizontalScale(uint scale)
|
||||
{
|
||||
if (scale == d->horizontalScale)
|
||||
return;
|
||||
|
||||
d->horizontalScale = scale;
|
||||
updateDataBuffers();
|
||||
d->backgroundPixmap = QPixmap(); // we changed a paint setting, so reset the cache
|
||||
}
|
||||
|
||||
uint SignalPlotter::horizontalScale() const
|
||||
{
|
||||
return d->horizontalScale;
|
||||
}
|
||||
|
||||
void SignalPlotter::setShowVerticalLines(bool value)
|
||||
{
|
||||
if (d->showVerticalLines == value) return;
|
||||
d->showVerticalLines = value;
|
||||
d->backgroundPixmap = QPixmap(); // we changed a paint setting, so reset the cache
|
||||
}
|
||||
|
||||
bool SignalPlotter::showVerticalLines() const
|
||||
{
|
||||
return d->showVerticalLines;
|
||||
}
|
||||
|
||||
void SignalPlotter::setVerticalLinesColor(const QColor &color)
|
||||
{
|
||||
if (d->verticalLinesColor == color) return;
|
||||
d->verticalLinesColor = color;
|
||||
d->backgroundPixmap = QPixmap(); // we changed a paint setting, so reset the cache
|
||||
}
|
||||
|
||||
QColor SignalPlotter::verticalLinesColor() const
|
||||
{
|
||||
return d->verticalLinesColor;
|
||||
}
|
||||
|
||||
void SignalPlotter::setVerticalLinesDistance(uint distance)
|
||||
{
|
||||
if (distance == d->verticalLinesDistance) return;
|
||||
d->verticalLinesDistance = distance;
|
||||
d->backgroundPixmap = QPixmap(); // we changed a paint setting, so reset the cache
|
||||
}
|
||||
|
||||
uint SignalPlotter::verticalLinesDistance() const
|
||||
{
|
||||
return d->verticalLinesDistance;
|
||||
}
|
||||
|
||||
void SignalPlotter::setVerticalLinesScroll(bool value)
|
||||
{
|
||||
if (value == d->verticalLinesScroll) return;
|
||||
d->verticalLinesScroll = value;
|
||||
d->backgroundPixmap = QPixmap(); // we changed a paint setting, so reset the cache
|
||||
}
|
||||
|
||||
bool SignalPlotter::verticalLinesScroll() const
|
||||
{
|
||||
return d->verticalLinesScroll;
|
||||
}
|
||||
|
||||
void SignalPlotter::setShowHorizontalLines(bool value)
|
||||
{
|
||||
if (value == d->showHorizontalLines) return;
|
||||
d->showHorizontalLines = value;
|
||||
d->backgroundPixmap = QPixmap(); // we changed a paint setting, so reset the cache
|
||||
}
|
||||
|
||||
bool SignalPlotter::showHorizontalLines() const
|
||||
{
|
||||
return d->showHorizontalLines;
|
||||
}
|
||||
|
||||
void SignalPlotter::setFontColor(const QColor &color)
|
||||
{
|
||||
d->fontColor = color;
|
||||
}
|
||||
|
||||
QColor SignalPlotter::fontColor() const
|
||||
{
|
||||
return d->fontColor;
|
||||
}
|
||||
|
||||
void SignalPlotter::setHorizontalLinesColor(const QColor &color)
|
||||
{
|
||||
if (color == d->horizontalLinesColor) return;
|
||||
d->horizontalLinesColor = color;
|
||||
d->backgroundPixmap = QPixmap(); // we changed a paint setting, so reset the cache
|
||||
}
|
||||
|
||||
QColor SignalPlotter::horizontalLinesColor() const
|
||||
{
|
||||
return d->horizontalLinesColor;
|
||||
}
|
||||
|
||||
void SignalPlotter::setHorizontalLinesCount(uint count)
|
||||
{
|
||||
if (count == d->horizontalLinesCount) return;
|
||||
d->horizontalLinesCount = count;
|
||||
d->backgroundPixmap = QPixmap(); // we changed a paint setting, so reset the cache
|
||||
calculateNiceRange();
|
||||
}
|
||||
|
||||
uint SignalPlotter::horizontalLinesCount() const
|
||||
{
|
||||
return d->horizontalLinesCount;
|
||||
}
|
||||
|
||||
void SignalPlotter::setShowLabels(bool value)
|
||||
{
|
||||
if (value == d->showLabels) return;
|
||||
d->showLabels = value;
|
||||
d->backgroundPixmap = QPixmap(); // we changed a paint setting, so reset the cache
|
||||
}
|
||||
|
||||
bool SignalPlotter::showLabels() const
|
||||
{
|
||||
return d->showLabels;
|
||||
}
|
||||
|
||||
void SignalPlotter::setShowTopBar(bool value)
|
||||
{
|
||||
if (d->showTopBar == value) return;
|
||||
d->showTopBar = value;
|
||||
d->backgroundPixmap = QPixmap(); // we changed a paint setting, so reset the cache
|
||||
}
|
||||
|
||||
bool SignalPlotter::showTopBar() const
|
||||
{
|
||||
return d->showTopBar;
|
||||
}
|
||||
|
||||
void SignalPlotter::setFont(const QFont &font)
|
||||
{
|
||||
d->font = font;
|
||||
d->backgroundPixmap = QPixmap(); // we changed a paint setting, so reset the cache
|
||||
}
|
||||
|
||||
QFont SignalPlotter::font() const
|
||||
{
|
||||
return d->font;
|
||||
}
|
||||
|
||||
QString SignalPlotter::svgBackground()
|
||||
{
|
||||
return d->svgFilename;
|
||||
}
|
||||
|
||||
void SignalPlotter::setSvgBackground(const QString &filename)
|
||||
{
|
||||
if (d->svgFilename == filename) return;
|
||||
|
||||
if (!filename.isEmpty() && filename[0] == '/') {
|
||||
KStandardDirs* kstd = KGlobal::dirs();
|
||||
d->svgFilename = kstd->findResource("data", "ksysguard/" + filename);
|
||||
} else {
|
||||
d->svgFilename = filename;
|
||||
}
|
||||
|
||||
if (!d->svgFilename.isEmpty())
|
||||
{
|
||||
if (d->svgBackground) delete d->svgBackground;
|
||||
d->svgBackground = new Svg(d->svgFilename);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void SignalPlotter::setBackgroundColor(const QColor &color)
|
||||
{
|
||||
if (color == d->backgroundColor) return;
|
||||
d->backgroundColor = color;
|
||||
d->backgroundPixmap = QPixmap(); // we changed a paint setting, so reset the cache
|
||||
}
|
||||
|
||||
QColor SignalPlotter::backgroundColor() const
|
||||
{
|
||||
return d->backgroundColor;
|
||||
}
|
||||
|
||||
void SignalPlotter::setThinFrame(bool set)
|
||||
{
|
||||
if (d->showThinFrame == set) return;
|
||||
d->showThinFrame = set;
|
||||
d->backgroundPixmap = QPixmap(); // we changed a paint setting, so reset the cache
|
||||
}
|
||||
|
||||
void SignalPlotter::setStackPlots(bool stack)
|
||||
{
|
||||
d->stackPlots = stack;
|
||||
d->fillPlots = stack;
|
||||
}
|
||||
|
||||
bool SignalPlotter::stackPlots() const
|
||||
{
|
||||
return d->stackPlots;
|
||||
}
|
||||
|
||||
void SignalPlotter::updateDataBuffers()
|
||||
{
|
||||
// This is called when the widget has resized
|
||||
//
|
||||
// Determine new number of samples first.
|
||||
// +0.5 to ensure rounding up
|
||||
// +4 for extra data points so there is
|
||||
// 1) no wasted space and
|
||||
// 2) no loss of precision when drawing the first data point.
|
||||
d->samples = static_cast<uint>(((size().width() - 2) /
|
||||
d->horizontalScale) + 4.5);
|
||||
}
|
||||
|
||||
QPixmap SignalPlotter::getSnapshotImage(uint w, uint height)
|
||||
{
|
||||
uint horizontalStep = (uint) ((1.0*w/size().width())+0.5); // get the closest integer horizontal step
|
||||
uint newWidth = (uint) (horizontalStep * size().width());
|
||||
QPixmap image = QPixmap(newWidth, height);
|
||||
QPainter p(&image);
|
||||
drawWidget(&p, newWidth, height, newWidth);
|
||||
return image;
|
||||
}
|
||||
|
||||
void SignalPlotter::paintWidget(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
|
||||
{
|
||||
Q_UNUSED(option);
|
||||
Q_UNUSED(widget);
|
||||
|
||||
uint w = (uint) size().width();
|
||||
uint h = (uint) size().height();
|
||||
|
||||
// Do not do repaints when the widget is not yet setup properly.
|
||||
if (w <= 2)
|
||||
return;
|
||||
|
||||
drawWidget(painter, w, h, d->horizontalScale);
|
||||
}
|
||||
|
||||
void SignalPlotter::drawWidget(QPainter *p, uint w, uint height, int horizontalScale)
|
||||
{
|
||||
uint h = height; // h will become the height of just the bit we draw the plots in
|
||||
p->setFont(d->font);
|
||||
|
||||
uint fontheight = p->fontMetrics().height();
|
||||
if (d->verticalMin < d->niceVertMin || d->verticalMax > d->niceVertMax || d->verticalMax < (d->niceVertRange*0.75 + d->niceVertMin) || d->niceVertRange == 0)
|
||||
calculateNiceRange();
|
||||
QPen pen;
|
||||
pen.setWidth(1);
|
||||
pen.setCapStyle(Qt::RoundCap);
|
||||
p->setPen(pen);
|
||||
|
||||
uint top = p->pen().width() / 2; // The y position of the top of the graph. Basically this is one more than the height of the top bar
|
||||
h-= top;
|
||||
|
||||
// Check if there's enough room to actually show a top bar.
|
||||
// Must be enough room for a bar at the top, plus horizontal
|
||||
// lines each of a size with room for a scale.
|
||||
bool showTopBar = d->showTopBar && h > (fontheight/*top bar size*/ +5/*smallest reasonable size for a graph*/);
|
||||
if (showTopBar) {
|
||||
top += fontheight; // The top bar has the same height as fontheight. Thus the top of the graph is at fontheight
|
||||
h -= fontheight;
|
||||
}
|
||||
if (d->backgroundPixmap.isNull() || (uint)d->backgroundPixmap.size().height() != height || (uint)d->backgroundPixmap.size().width() != w) { // recreate on resize etc
|
||||
d->backgroundPixmap = QPixmap(w, height);
|
||||
QPainter pCache(&d->backgroundPixmap);
|
||||
pCache.setRenderHint(QPainter::Antialiasing, false);
|
||||
pCache.setFont(d->font);
|
||||
|
||||
drawBackground(&pCache, w, height);
|
||||
|
||||
if (d->showThinFrame) {
|
||||
drawThinFrame(&pCache, w, height);
|
||||
// We have a 'frame' in the bottom and right - so subtract them from the view
|
||||
h--;
|
||||
w--;
|
||||
pCache.setClipRect(0, 0, w, height-1);
|
||||
}
|
||||
|
||||
if (showTopBar) {
|
||||
int seperatorX = w / 2;
|
||||
drawTopBarFrame(&pCache, seperatorX, top);
|
||||
}
|
||||
|
||||
// Draw scope-like grid vertical lines if it doesn't move.
|
||||
// If it does move, draw it in the dynamic part of the code.
|
||||
if (!d->verticalLinesScroll && d->showVerticalLines && w > 60)
|
||||
drawVerticalLines(&pCache, top, w, h);
|
||||
|
||||
if (d->showHorizontalLines)
|
||||
drawHorizontalLines(&pCache, top, w, h);
|
||||
|
||||
} else {
|
||||
if (d->showThinFrame) {
|
||||
// We have a 'frame' in the bottom and right - so subtract them from the view
|
||||
h--;
|
||||
w--;
|
||||
}
|
||||
}
|
||||
p->drawPixmap(0,0, d->backgroundPixmap);
|
||||
p->setRenderHint(QPainter::Antialiasing, true);
|
||||
|
||||
if (showTopBar) {
|
||||
int seperatorX = w / 2;
|
||||
int topBarWidth = w - seperatorX -2;
|
||||
drawTopBarContents(p, seperatorX, topBarWidth, top -1);
|
||||
}
|
||||
|
||||
p->setClipRect(0, top, w, h);
|
||||
// Draw scope-like grid vertical lines
|
||||
if (d->verticalLinesScroll && d->showVerticalLines && w > 60)
|
||||
drawVerticalLines(p, top, w, h);
|
||||
|
||||
drawPlots(p, top, w, h, horizontalScale);
|
||||
|
||||
if (d->showLabels && w > 60 && h > (fontheight + 1)) // if there's room to draw the labels, then draw them!
|
||||
drawAxisText(p, top, h);
|
||||
|
||||
}
|
||||
|
||||
void SignalPlotter::drawBackground(QPainter *p, int w, int h)
|
||||
{
|
||||
p->fillRect(0,0,w, h, d->backgroundColor);
|
||||
if (d->svgBackground)
|
||||
{
|
||||
d->svgBackground->resize(w, h);
|
||||
d->svgBackground->paint(p, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void SignalPlotter::drawThinFrame(QPainter *p, int w, int h)
|
||||
{
|
||||
// Draw white line along the bottom and the right side of the
|
||||
// widget to create a 3D like look.
|
||||
p->setPen(kapp->palette().color(QPalette::Light));
|
||||
p->drawLine(0, h - 1, w - 1, h - 1);
|
||||
p->drawLine(w - 1, 0, w - 1, h - 1);
|
||||
}
|
||||
|
||||
void SignalPlotter::calculateNiceRange()
|
||||
{
|
||||
d->niceVertRange = d->verticalMax - d->verticalMin;
|
||||
// If the range is too small we will force it to 1.0 since it
|
||||
// looks a lot nicer.
|
||||
if (d->niceVertRange < 0.000001)
|
||||
d->niceVertRange = 1.0;
|
||||
|
||||
d->niceVertMin = d->verticalMin;
|
||||
if (d->verticalMin != 0.0) {
|
||||
double dim = pow(10, floor(log10(fabs(d->verticalMin)))) / 2;
|
||||
if (d->verticalMin < 0.0)
|
||||
d->niceVertMin = dim * floor(d->verticalMin / dim);
|
||||
else
|
||||
d->niceVertMin = dim * ceil(d->verticalMin / dim);
|
||||
d->niceVertRange = d->verticalMax - d->niceVertMin;
|
||||
if (d->niceVertRange < 0.000001)
|
||||
d->niceVertRange = 1.0;
|
||||
}
|
||||
// Massage the range so that the grid shows some nice values.
|
||||
double step = d->niceVertRange / (d->scaledBy*(d->horizontalLinesCount+1));
|
||||
int logdim = (int)floor(log10(step));
|
||||
double dim = pow(10, logdim) / 2;
|
||||
int a = (int)ceil(step / dim);
|
||||
if (logdim >= 0)
|
||||
d->precision = 0;
|
||||
else if (a % 2 == 0){
|
||||
d->precision =-logdim;
|
||||
} else {
|
||||
d->precision = 1-logdim;
|
||||
}
|
||||
d->niceVertRange = d->scaledBy*dim * a * (d->horizontalLinesCount+1);
|
||||
d->niceVertMax = d->niceVertMin + d->niceVertRange;
|
||||
}
|
||||
|
||||
|
||||
void SignalPlotter::drawTopBarFrame(QPainter *p, int seperatorX, int height)
|
||||
{
|
||||
// Draw horizontal bar with current sensor values at top of display.
|
||||
// Remember that it has a height of 'height'. Thus the lowest pixel
|
||||
// it can draw on is height-1 since we count from 0.
|
||||
p->setPen(Qt::NoPen);
|
||||
p->setPen(d->fontColor);
|
||||
p->drawText(0, 1, seperatorX, height, Qt::AlignCenter, d->title);
|
||||
p->setPen(d->horizontalLinesColor);
|
||||
p->drawLine(seperatorX - 1, 1, seperatorX - 1, height-1);
|
||||
}
|
||||
|
||||
void SignalPlotter::drawTopBarContents(QPainter *p, int x, int width, int height)
|
||||
{
|
||||
// The height is the height of the contents, so this will be one pixel less than the height of the topbar
|
||||
double bias = -d->niceVertMin;
|
||||
double scaleFac = width / d->niceVertRange;
|
||||
// The top bar shows the current values of all the plot data.
|
||||
// This iterates through each different plot and plots the newest data for each.
|
||||
if (!d->plotData.isEmpty()) {
|
||||
QList<double> newestData = d->plotData.first();
|
||||
for (int i = newestData.count()-1; i >= 0; --i) {
|
||||
double newest_datapoint = newestData.at(i);
|
||||
int start = x + (int)(bias * scaleFac);
|
||||
int end = x + (int)((bias += newest_datapoint) * scaleFac);
|
||||
int start2 = qMin(start,end);
|
||||
end = qMax(start,end);
|
||||
start = start2;
|
||||
|
||||
// If the rect is wider than 2 pixels we draw only the last
|
||||
// pixels with the bright color. The rest is painted with
|
||||
// a 50% darker color.
|
||||
|
||||
p->setPen(Qt::NoPen);
|
||||
QLinearGradient linearGrad(QPointF(start,1), QPointF(end, 1));
|
||||
linearGrad.setColorAt(0, d->plotColors[i].darkColor);
|
||||
linearGrad.setColorAt(1, d->plotColors[i].color);
|
||||
p->fillRect(start, 1, end - start, height-1, QBrush(linearGrad));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SignalPlotter::drawVerticalLines(QPainter *p, int top, int w, int h)
|
||||
{
|
||||
p->setPen(d->verticalLinesColor);
|
||||
for (int x = d->verticalLinesOffset; x < (w - 2); x += d->verticalLinesDistance)
|
||||
p->drawLine(w - x, top, w - x, h + top -1);
|
||||
}
|
||||
|
||||
void SignalPlotter::drawPlots(QPainter *p, int top, int w, int h, int horizontalScale)
|
||||
{
|
||||
Q_ASSERT(d->niceVertRange != 0); if (d->niceVertRange == 0) d->niceVertRange = 1;
|
||||
double scaleFac = (h-1) / d->niceVertRange;
|
||||
|
||||
int xPos = 0;
|
||||
QList< QList<double> >::Iterator it = d->plotData.begin();
|
||||
|
||||
p->setPen(Qt::NoPen);
|
||||
// In autoRange mode we determine the range and plot the values in
|
||||
// one go. This is more efficiently than running through the
|
||||
// buffers twice but we do react on recently discarded samples as
|
||||
// well as new samples one plot too late. So the range is not
|
||||
// correct if the recently discarded samples are larger or smaller
|
||||
// than the current extreme values. But we can probably live with
|
||||
// this.
|
||||
|
||||
// These values aren't used directly anywhere. Instead we call
|
||||
// calculateNiceRange() which massages these values into a nicer
|
||||
// values. Rounding etc. This means it's safe to change these values
|
||||
// without affecting any other drawings.
|
||||
if (d->useAutoRange)
|
||||
d->verticalMin = d->verticalMax = 0.0;
|
||||
|
||||
// d->bezierCurveOffset is how many points we have at the start.
|
||||
// All the bezier curves are in groups of 3, with the first of the next group being the last point
|
||||
// of the previous group
|
||||
|
||||
// Example, when d->bezierCurveOffset == 0, and we have data, then just plot a normal bezier curve.
|
||||
// (we will have at least 3 points in this case)
|
||||
// When d->bezierCurveOffset == 1, then we want a bezier curve that uses the first data point and
|
||||
// the second data point. Then the next group starts from the second data point.
|
||||
// When d->bezierCurveOffset == 2, then we want a bezier curve that uses the first, second and third data.
|
||||
for (uint i = 0; it != d->plotData.end() && i < d->samples; ++i) {
|
||||
QPen pen;
|
||||
pen.setWidth(1);
|
||||
pen.setCapStyle(Qt::FlatCap);
|
||||
|
||||
// We will plot 1 bezier curve for every 3 points, with the 4th point being the end
|
||||
// of one bezier curve and the start of the second. This does means the bezier curves
|
||||
// will not join nicely, but it should be better than nothing.
|
||||
QList<double> datapoints = *it;
|
||||
QList<double> prev_datapoints = datapoints;
|
||||
QList<double> prev_prev_datapoints = datapoints;
|
||||
QList<double> prev_prev_prev_datapoints = datapoints;
|
||||
|
||||
if (i == 0 && d->bezierCurveOffset>0) {
|
||||
// We are plotting an incomplete bezier curve - we don't have all the data we want.
|
||||
// Try to cope.
|
||||
xPos += horizontalScale*d->bezierCurveOffset;
|
||||
if (d->bezierCurveOffset == 1) {
|
||||
prev_datapoints = *it;
|
||||
++it; // Now we are on the first element of the next group, if it exists
|
||||
if (it != d->plotData.end()) {
|
||||
prev_prev_prev_datapoints = prev_prev_datapoints = *it;
|
||||
} else {
|
||||
prev_prev_prev_datapoints = prev_prev_datapoints = prev_datapoints;
|
||||
}
|
||||
} else {
|
||||
// d->bezierCurveOffset must be 2 now
|
||||
prev_datapoints = *it;
|
||||
Q_ASSERT(it != d->plotData.end());
|
||||
++it;
|
||||
prev_prev_datapoints = *it;
|
||||
Q_ASSERT(it != d->plotData.end());
|
||||
++it; // Now we are on the first element of the next group, if it exists
|
||||
if (it != d->plotData.end()) {
|
||||
prev_prev_prev_datapoints = *it;
|
||||
} else {
|
||||
prev_prev_prev_datapoints = prev_prev_datapoints;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// We have a group of 3 points at least. That's 1 start point and 2 control points.
|
||||
xPos += horizontalScale*3;
|
||||
it++;
|
||||
if (it != d->plotData.end()) {
|
||||
prev_datapoints = *it;
|
||||
it++;
|
||||
if (it != d->plotData.end()) {
|
||||
prev_prev_datapoints = *it;
|
||||
it++; // We are now on the next set of data points
|
||||
if (it != d->plotData.end()) {
|
||||
// We have this datapoint, so use it for our finish point
|
||||
prev_prev_prev_datapoints = *it;
|
||||
} else {
|
||||
// We don't have the next set, so use our last control point as our finish point
|
||||
prev_prev_prev_datapoints = prev_prev_datapoints;
|
||||
}
|
||||
} else {
|
||||
prev_prev_prev_datapoints = prev_prev_datapoints = prev_datapoints;
|
||||
}
|
||||
} else {
|
||||
prev_prev_prev_datapoints = prev_prev_datapoints = prev_datapoints = datapoints;
|
||||
}
|
||||
}
|
||||
|
||||
float x0 = w - xPos + 3.0*horizontalScale;
|
||||
float x1 = w - xPos + 2.0*horizontalScale;
|
||||
float x2 = w - xPos + 1.0*horizontalScale;
|
||||
float x3 = w - xPos;
|
||||
float y0 = h -1 + top;
|
||||
float y1 = y0;
|
||||
float y2 = y0;
|
||||
float y3 = y0;
|
||||
|
||||
int offset = 0; // Our line is 2 pixels thick. This means that when we draw the area, we need to offset
|
||||
double max_y=0;
|
||||
double min_y=0;
|
||||
for (int j = qMin(datapoints.size(), d->plotColors.size())-1; j >=0 ; --j) {
|
||||
if (d->useAutoRange) {
|
||||
// If we use autorange, then we need to prepare the min and max values for _next_ time we paint.
|
||||
// If we are stacking the plots, then we need to add the maximums together.
|
||||
double current_maxvalue = qMax(datapoints[j], qMax(prev_datapoints[j], qMax(prev_prev_datapoints[j], prev_prev_prev_datapoints[j])));
|
||||
double current_minvalue = qMin(datapoints[j], qMin(prev_datapoints[j], qMin(prev_prev_datapoints[j], prev_prev_prev_datapoints[j])));
|
||||
d->verticalMax = qMax(d->verticalMax, current_maxvalue);
|
||||
d->verticalMin = qMin(d->verticalMin, current_maxvalue);
|
||||
if (d->stackPlots) {
|
||||
max_y += current_maxvalue;
|
||||
min_y += current_minvalue;
|
||||
}
|
||||
}
|
||||
|
||||
// Draw polygon only if enough data points are available.
|
||||
if (j < prev_prev_prev_datapoints.count() &&
|
||||
j < prev_prev_datapoints.count() &&
|
||||
j < prev_datapoints.count()) {
|
||||
|
||||
QPolygon curve(4);
|
||||
|
||||
// The height of the whole widget is h+top-> The height of the area we are plotting in is just h.
|
||||
// The y coordinate system starts from the top, so at the bottom the y coordinate is h+top.
|
||||
// So to draw a point at value y', we need to put this at h+top-y'
|
||||
float delta_y0;
|
||||
delta_y0 = (datapoints[j] - d->niceVertMin)*scaleFac;
|
||||
|
||||
float delta_y1;
|
||||
delta_y1 = (prev_datapoints[j] - d->niceVertMin)*scaleFac;
|
||||
|
||||
float delta_y2;
|
||||
delta_y2 = (prev_prev_datapoints[j] - d->niceVertMin)*scaleFac;
|
||||
|
||||
float delta_y3;
|
||||
delta_y3 = (prev_prev_prev_datapoints[j] - d->niceVertMin)*scaleFac;
|
||||
|
||||
QPainterPath path;
|
||||
if (d->stackPlots && offset) {
|
||||
// we don't want the lines to overdraw each other. This isn't a great solution though :(
|
||||
if (delta_y0 < 3) delta_y0=3;
|
||||
if (delta_y1 < 3) delta_y1=3;
|
||||
if (delta_y2 < 3) delta_y2=3;
|
||||
if (delta_y3 < 3) delta_y3=3;
|
||||
}
|
||||
path.moveTo(x0,y0-delta_y0);
|
||||
path.cubicTo(x1,y1-delta_y1,x2,y2-delta_y2,x3,y3-delta_y3);
|
||||
|
||||
if (d->fillPlots) {
|
||||
QPainterPath path2(path);
|
||||
QLinearGradient myGradient(0,(h-1+top),0,(h-1+top)/5);
|
||||
Q_ASSERT(d->plotColors.size() >= j);
|
||||
QColor c0(d->plotColors[j].darkColor);
|
||||
QColor c1(d->plotColors[j].color);
|
||||
c0.setAlpha(150);
|
||||
c1.setAlpha(150);
|
||||
myGradient.setColorAt(0, c0);
|
||||
myGradient.setColorAt(1, c1);
|
||||
|
||||
path2.lineTo(x3,y3-offset);
|
||||
if (d->stackPlots)
|
||||
path2.cubicTo(x2,y2-offset,x1,y1-offset,x0,y0-offset); // offset is set to 1 after the first plot is drawn, so we don't trample on top of the 2pt thick line
|
||||
else
|
||||
path2.lineTo(x0,y0-1);
|
||||
p->setBrush(myGradient);
|
||||
p->setPen(Qt::NoPen);
|
||||
p->drawPath(path2);
|
||||
}
|
||||
p->setBrush(Qt::NoBrush);
|
||||
Q_ASSERT(d->plotColors.size() >= j);
|
||||
pen.setColor(d->plotColors[j].color);
|
||||
p->setPen(pen);
|
||||
p->drawPath(path);
|
||||
|
||||
if (d->stackPlots) {
|
||||
// We can draw the plots stacked on top of each other.
|
||||
// This means that say plot 0 has the value 2 and plot
|
||||
// 1 has the value 3, then we plot plot 0 at 2 and plot 1 at 2+3 = 5.
|
||||
y0-=delta_y0;
|
||||
y1-=delta_y1;
|
||||
y2-=delta_y2;
|
||||
y3-=delta_y3;
|
||||
offset = 1; // see the comment further up for int offset;
|
||||
}
|
||||
}
|
||||
if (d->useAutoRange && d->stackPlots) {
|
||||
d->verticalMax = qMax(max_y, d->verticalMax);
|
||||
d->verticalMin = qMin(min_y, d->verticalMin);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SignalPlotter::drawAxisText(QPainter *p, int top, int h)
|
||||
{
|
||||
// Draw horizontal lines and values. Lines are always drawn.
|
||||
// Values are only draw when width is greater than 60.
|
||||
QString val;
|
||||
|
||||
// top = 0 or font.height depending on whether there's a topbar or not
|
||||
// h = graphing area.height - i.e. the actual space we have to draw inside
|
||||
// Note we are drawing from 0,0 as the top left corner. So we have to add on top
|
||||
// to get to the top of where we are drawing so top+h is the height of the widget.
|
||||
p->setPen(d->fontColor);
|
||||
double stepsize = d->niceVertRange/(d->scaledBy*(d->horizontalLinesCount+1));
|
||||
int step = (int)ceil((d->horizontalLinesCount+1) * (p->fontMetrics().height() + p->fontMetrics().leading()/2.0) / h);
|
||||
if (step ==0) step = 1;
|
||||
for (int y = d->horizontalLinesCount+1; y >= 1; y-= step) {
|
||||
int y_coord = top + (y * (h-1)) / (d->horizontalLinesCount+1); // Make sure it's y*h first to avoid rounding bugs
|
||||
if (y_coord - p->fontMetrics().ascent() < top) continue; // at most, only allow 4 pixels of the text to be covered up by the top bar. Otherwise just don't bother to draw it
|
||||
double value;
|
||||
if ((uint)y == d->horizontalLinesCount+1)
|
||||
value = d->niceVertMin; // sometimes using the formulas gives us a value very slightly off
|
||||
else
|
||||
value = d->niceVertMax/d->scaledBy - y * stepsize;
|
||||
|
||||
QString number = KGlobal::locale()->formatNumber(value, d->precision);
|
||||
val = QString("%1 %2").arg(number, d->unit);
|
||||
p->drawText(6, y_coord - 3, val);
|
||||
}
|
||||
}
|
||||
|
||||
void SignalPlotter::drawHorizontalLines(QPainter *p, int top, int w, int h)
|
||||
{
|
||||
p->setPen(d->horizontalLinesColor);
|
||||
for (uint y = 0; y <= d->horizontalLinesCount+1; y++) {
|
||||
// note that the y_coord starts from 0. so we draw from pixel number 0 to h-1. Thus the -1 in the y_coord
|
||||
int y_coord = top + (y * (h-1)) / (d->horizontalLinesCount+1); // Make sure it's y*h first to avoid rounding bugs
|
||||
p->drawLine(0, y_coord, w - 2, y_coord);
|
||||
}
|
||||
}
|
||||
|
||||
double SignalPlotter::lastValue(uint i) const
|
||||
{
|
||||
if (d->plotData.isEmpty() || d->plotData.first().size() <= (int) i) return 0;
|
||||
return d->plotData.first()[i];
|
||||
}
|
||||
|
||||
QString SignalPlotter::lastValueAsString(uint i) const
|
||||
{
|
||||
if (d->plotData.isEmpty()) return QString();
|
||||
double value = d->plotData.first()[i] / d->scaledBy; // retrieve the newest value for this plot then scale it correct
|
||||
QString number = KGlobal::locale()->formatNumber(value, (value >= 100)?0:2);
|
||||
return QString("%1 %2").arg(number, d->unit);
|
||||
}
|
||||
|
||||
} // Plasma namespace
|
412
widgets/signalplotter.h
Normal file
412
widgets/signalplotter.h
Normal file
@ -0,0 +1,412 @@
|
||||
/*
|
||||
* KSysGuard, the KDE System Guard
|
||||
*
|
||||
* Copyright 1999 - 2001 Chris Schlaeger <cs@kde.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Library General Public License version 2 as
|
||||
* published by the Free Software Foundation
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef SIGNALPLOTTER_H
|
||||
#define SIGNALPLOTTER_H
|
||||
|
||||
#include <plasma/widgets/widget.h>
|
||||
|
||||
namespace Plasma
|
||||
{
|
||||
|
||||
struct PlotColor
|
||||
{
|
||||
QColor color;
|
||||
QColor darkColor;
|
||||
};
|
||||
|
||||
class PLASMA_EXPORT SignalPlotter : public Widget
|
||||
{
|
||||
|
||||
public:
|
||||
SignalPlotter(Widget *parent = 0);
|
||||
~SignalPlotter();
|
||||
|
||||
Qt::Orientations expandingDirections() const;
|
||||
|
||||
/**
|
||||
* Add a new line to the graph plotter, with the specified color.
|
||||
* Note that the order you add the plots must be the same order that
|
||||
* the same data is given in (unless you reorder the plots).
|
||||
* @param color the color to use for this plot
|
||||
*/
|
||||
void addPlot(const QColor &color);
|
||||
|
||||
/**
|
||||
* Add data to the graph, and advance the graph by one time period.
|
||||
* The data must be given as a list in the same order that the plots were
|
||||
* added (or consequently reordered).
|
||||
* @param samples a list with the new value for each plot
|
||||
*/
|
||||
void addSample(const QList<double> &samples);
|
||||
|
||||
/**
|
||||
* Reorder the plots into the order given. For example:
|
||||
* \code
|
||||
* KSignalPlotter *s = KSignalPlotter(parent);
|
||||
* s->addPlot(Qt::Blue);
|
||||
* s->addPlot(Qt::Green);
|
||||
* QList neworder;
|
||||
* neworder << 1 << 0;
|
||||
* reorderPlots(newOrder);
|
||||
* //Now the order is Green then Blue
|
||||
* \endcode
|
||||
* @param newOrder a list with the new position of each plot
|
||||
*/
|
||||
void reorderPlots(const QList<uint>& newOrder);
|
||||
|
||||
/**
|
||||
* Removes the plot at the specified index.
|
||||
* @param pos the index of the plot to be removed
|
||||
*/
|
||||
void removePlot(uint pos);
|
||||
|
||||
/**
|
||||
* Return the list of plot colors, in the order that the plots
|
||||
* were added (or later reordered).
|
||||
* @return a list containing the color of every plot
|
||||
*/
|
||||
QList<PlotColor> &plotColors();
|
||||
|
||||
/**
|
||||
* Set the title of the graph. Drawn in the top left.
|
||||
* @param title the title to use in the plotter
|
||||
*/
|
||||
void setTitle(const QString &title);
|
||||
|
||||
/**
|
||||
* Get the title of the graph. Drawn in the top left.
|
||||
* @return the title to use in the plotter
|
||||
*/
|
||||
QString title() const;
|
||||
|
||||
/**
|
||||
* Set the units. Drawn on the vertical axis of the graph.
|
||||
* Must be already translated into the local language.
|
||||
* @param unit the unit string to use
|
||||
*/
|
||||
void setUnit(const QString &unit);
|
||||
|
||||
/**
|
||||
* Return the units used on the vertical axis of the graph.
|
||||
* @return the unit string used
|
||||
*/
|
||||
QString unit() const;
|
||||
|
||||
/**
|
||||
* Scale all the values down by the given amount. This is useful
|
||||
* when the data is given in, say, kilobytes, but you set the
|
||||
* units as megabytes. Thus you would have to call this with @p value
|
||||
* set to 1024. This affects all the data already entered.
|
||||
* @param delta the factor used to scale down the values
|
||||
*/
|
||||
void scale(qreal delta);
|
||||
|
||||
/**
|
||||
* Amount scaled down by. @see scale
|
||||
* @return the factor used to scale down the values
|
||||
*/
|
||||
qreal scaledBy() const;
|
||||
|
||||
/**
|
||||
* Set the minimum and maximum values on the vertical axis
|
||||
* automatically from the data available.
|
||||
* @param value true if the plotter should calculate its own
|
||||
* min and max values, otherwise false
|
||||
*/
|
||||
void setUseAutoRange(bool value);
|
||||
|
||||
/**
|
||||
* Whether the vertical axis range is set automatically.
|
||||
* @return true if the plotter calculates its own min and max
|
||||
* values, otherwise false
|
||||
*/
|
||||
bool useAutoRange() const;
|
||||
|
||||
/**
|
||||
* Change the minimum and maximum values drawn on the graph.
|
||||
* Note that these values are sanitised. For example, if you
|
||||
* set the minimum as 3, and the maximum as 97, then the graph
|
||||
* would be drawn between 0 and 100. The algorithm to determine
|
||||
* this "nice range" attempts to minimize the number of non-zero
|
||||
* digits.
|
||||
*
|
||||
* Use setAutoRange instead to determine the range automatically
|
||||
* from the data.
|
||||
* @param min the minimum value to use for the vertical axis
|
||||
* @param max the maximum value to use for the vertical axis
|
||||
*/
|
||||
void setVerticalRange(double min, double max);
|
||||
|
||||
/**
|
||||
* Get the min value of the vertical axis. @see changeRange
|
||||
* @return the minimum value to use for the vertical axis
|
||||
*/
|
||||
double verticalMinValue() const;
|
||||
|
||||
/**
|
||||
* Get the max value of the vertical axis. @see changeRange
|
||||
* @return the maximum value to use for the vertical axis
|
||||
*/
|
||||
double verticalMaxValue() const;
|
||||
|
||||
/**
|
||||
* Set the number of pixels horizontally between data points
|
||||
* @param scale the number of pixel to draw between two data points
|
||||
*/
|
||||
void setHorizontalScale(uint scale);
|
||||
|
||||
/**
|
||||
* The number of pixels horizontally between data points
|
||||
* @return the number of pixel drawn between two data points
|
||||
*/
|
||||
uint horizontalScale() const;
|
||||
|
||||
/**
|
||||
* Whether to draw the vertical grid lines
|
||||
* @param value true if the lines should be drawn, otherwise false
|
||||
*/
|
||||
void setShowVerticalLines(bool value);
|
||||
|
||||
/**
|
||||
* Whether the vertical grid lines will be drawn
|
||||
* @return true if the lines will be drawn, otherwise false
|
||||
*/
|
||||
bool showVerticalLines() const;
|
||||
|
||||
/**
|
||||
* The color of the vertical grid lines
|
||||
* @param color the color used to draw the vertical grid lines
|
||||
*/
|
||||
void setVerticalLinesColor(const QColor &color);
|
||||
|
||||
/**
|
||||
* The color of the vertical grid lines
|
||||
* @return the color used to draw the vertical grid lines
|
||||
*/
|
||||
QColor verticalLinesColor() const;
|
||||
|
||||
/**
|
||||
* The horizontal distance between the vertical grid lines
|
||||
* @param distance the distance between two vertical grid lines
|
||||
*/
|
||||
void setVerticalLinesDistance(uint distance);
|
||||
|
||||
/**
|
||||
* The horizontal distance between the vertical grid lines
|
||||
* @return the distance between two vertical grid lines
|
||||
*/
|
||||
uint verticalLinesDistance() const;
|
||||
|
||||
/**
|
||||
* Whether the vertical lines move with the data
|
||||
* @param value true if the vertical lines should move with the data
|
||||
*/
|
||||
void setVerticalLinesScroll(bool value);
|
||||
|
||||
/**
|
||||
* Whether the vertical lines move with the data
|
||||
* @return true if the vertical lines will move with the data
|
||||
*/
|
||||
bool verticalLinesScroll() const;
|
||||
|
||||
/**
|
||||
* Whether to draw the horizontal grid lines
|
||||
* @param value true if the lines should be drawn, otherwise false
|
||||
*/
|
||||
void setShowHorizontalLines(bool value);
|
||||
/**
|
||||
* Whether to draw the horizontal grid lines
|
||||
* @return true if the lines will be drawn, otherwise false
|
||||
*/
|
||||
bool showHorizontalLines() const;
|
||||
|
||||
/**
|
||||
* The color of the horizontal grid lines
|
||||
* @param color the color used to draw the horizontal grid lines
|
||||
*/
|
||||
void setHorizontalLinesColor(const QColor &color);
|
||||
|
||||
/**
|
||||
* The color of the horizontal grid lines
|
||||
* @return the color used to draw the horizontal grid lines
|
||||
*/
|
||||
QColor horizontalLinesColor() const;
|
||||
|
||||
/**
|
||||
* The color of the font used for the axis
|
||||
* @param color the color used to draw the text of the vertical values
|
||||
*/
|
||||
void setFontColor(const QColor &color);
|
||||
|
||||
/**
|
||||
* The color of the font used for the axis
|
||||
* @return the color used to draw the text of the vertical values
|
||||
*/
|
||||
QColor fontColor() const;
|
||||
|
||||
/**
|
||||
* The font used for the axis
|
||||
* @param font the font used to draw the text of the vertical values
|
||||
*/
|
||||
void setFont(const QFont &font);
|
||||
|
||||
/**
|
||||
* The font used for the axis
|
||||
* @return the font used to draw the text of the vertical values
|
||||
*/
|
||||
QFont font() const;
|
||||
|
||||
/**
|
||||
* The number of horizontal lines to draw. Doesn't include the top
|
||||
* most and bottom most lines.
|
||||
* @param count the number of horizontal lines to draw
|
||||
*/
|
||||
void setHorizontalLinesCount(uint count);
|
||||
|
||||
/**
|
||||
* The number of horizontal lines to draw. Doesn't include the top
|
||||
* most and bottom most lines.
|
||||
* @return the number of horizontal lines that will be drawn
|
||||
*/
|
||||
uint horizontalLinesCount() const;
|
||||
|
||||
/**
|
||||
* Whether to show the vertical axis labels
|
||||
* @param value true if the values for the vertical axis should get drawn
|
||||
*/
|
||||
void setShowLabels(bool value);
|
||||
|
||||
/**
|
||||
* Whether to show the vertical axis labels
|
||||
* @return true if the values for the vertical axis will get drawn
|
||||
*/
|
||||
bool showLabels() const;
|
||||
|
||||
/**
|
||||
* Whether to show the title etc at the top. Even if set, it
|
||||
* won't be shown if there isn't room
|
||||
* @param value true if the topbar should be shown
|
||||
*/
|
||||
void setShowTopBar(bool value);
|
||||
|
||||
/**
|
||||
* Whether to show the title etc at the top. Even if set, it
|
||||
* won't be shown if there isn't room
|
||||
* @return true if the topbar will be shown
|
||||
*/
|
||||
bool showTopBar() const;
|
||||
|
||||
/**
|
||||
* The color to set the background. This might not be seen
|
||||
* if an svg is also set.
|
||||
* @param color the color to use for the plotter background
|
||||
*/
|
||||
void setBackgroundColor(const QColor &color);
|
||||
|
||||
/**
|
||||
* The color to set the background. This might not be seen
|
||||
* if an svg is also set.
|
||||
* @return the color of the plotter background
|
||||
*/
|
||||
QColor backgroundColor() const;
|
||||
|
||||
/**
|
||||
* The filename of the svg background. Set to empty to disable
|
||||
* again.
|
||||
* @param filename the SVG file to use as a background image
|
||||
*/
|
||||
void setSvgBackground(const QString &filename);
|
||||
|
||||
/**
|
||||
* The filename of the svg background. Set to empty to disable
|
||||
* again.
|
||||
* @return the file used as a background image
|
||||
*/
|
||||
QString svgBackground();
|
||||
|
||||
/**
|
||||
* Return the last value that we have for plot i. Returns 0 if not known.
|
||||
* @param i the plot we like to have the last value from
|
||||
* @return the last value of this plot or 0 if not found
|
||||
*/
|
||||
double lastValue(uint i) const;
|
||||
|
||||
/**
|
||||
* Return a translated string like: "34 %" or "100 KB" for plot i
|
||||
* @param i the plot we like to have the value as string from
|
||||
* @return the last value of this plot as a string
|
||||
*/
|
||||
QString lastValueAsString(uint i) const;
|
||||
|
||||
/**
|
||||
* Whether to show a white line on the left and bottom of the widget,
|
||||
* for a 3D effect
|
||||
* @param set true if the frame should get drawn
|
||||
*/
|
||||
void setThinFrame(bool set);
|
||||
|
||||
/**
|
||||
* Whether to stack the plots on top of each other. The first plot
|
||||
* added will be at the bottom. The next plot will be drawn on top,
|
||||
* and so on.
|
||||
* @param stack true if the plots should be stacked
|
||||
*/
|
||||
void setStackPlots(bool stack);
|
||||
|
||||
/**
|
||||
* Whether to stack the plots. @see setStackPlots
|
||||
* @return true if the plots will be stacked
|
||||
*/
|
||||
bool stackPlots() const;
|
||||
|
||||
/**
|
||||
* Render the graph to the specified width and height, and return it
|
||||
* as an image. This is useful, for example, if you draw a small version
|
||||
* of the graph, but then want to show a large version in a tooltip etc
|
||||
* @param width the width of the snapshot
|
||||
* @param height the height of the snapshot
|
||||
* @return a snapshot of the plotter as an image
|
||||
*/
|
||||
QPixmap getSnapshotImage(uint width, uint height);
|
||||
|
||||
protected:
|
||||
void updateDataBuffers();
|
||||
void calculateNiceRange();
|
||||
|
||||
void paintWidget(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
|
||||
|
||||
void drawWidget(QPainter *p, uint w, uint height, int horizontalScale);
|
||||
void drawBackground(QPainter *p, int w, int h);
|
||||
void drawThinFrame(QPainter *p, int w, int h);
|
||||
void drawTopBarFrame(QPainter *p, int seperatorX, int height);
|
||||
void drawTopBarContents(QPainter *p, int x, int width, int height);
|
||||
void drawVerticalLines(QPainter *p, int top, int w, int h);
|
||||
void drawPlots(QPainter *p, int top, int w, int h, int horizontalScale);
|
||||
void drawAxisText(QPainter *p, int top, int h);
|
||||
void drawHorizontalLines(QPainter *p, int top, int w, int h);
|
||||
|
||||
private:
|
||||
class Private;
|
||||
Private *const d;
|
||||
};
|
||||
|
||||
} // Plasma namespace
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user