From 9d2a22bbb0c35348dfde876f8227754881fe62df Mon Sep 17 00:00:00 2001 From: csoler Date: Sun, 12 Oct 2014 21:12:58 +0000 Subject: [PATCH] added missing file git-svn-id: http://svn.code.sf.net/p/retroshare/code/trunk@7605 b45a01b8-16f6-495d-af2f-9b41ad6348cc --- .../src/gui/common/RSGraphWidget.cpp | 369 ++++++++++++++++++ retroshare-gui/src/gui/common/RSGraphWidget.h | 177 +++++++++ 2 files changed, 546 insertions(+) create mode 100644 retroshare-gui/src/gui/common/RSGraphWidget.cpp create mode 100644 retroshare-gui/src/gui/common/RSGraphWidget.h diff --git a/retroshare-gui/src/gui/common/RSGraphWidget.cpp b/retroshare-gui/src/gui/common/RSGraphWidget.cpp new file mode 100644 index 000000000..775dd68ac --- /dev/null +++ b/retroshare-gui/src/gui/common/RSGraphWidget.cpp @@ -0,0 +1,369 @@ +/**************************************************************** + * This file is distributed under the following license: + * + * Copyright (C) 2014 RetroShare Team + * Copyright (c) 2006-2007, crypton + * Copyright (c) 2006, Matt Edman, Justin Hipple + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ****************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include "RSGraphWidget.h" + +RSGraphSource::RSGraphSource() +{ + _time_limit_msecs = 10*1000 ; + _update_period_msecs = 1*1000 ; + _time_orig_msecs = QDateTime::currentMSecsSinceEpoch() ; + _timer = new QTimer ; + QObject::connect(_timer,SIGNAL(timeout()),this,SLOT(update())) ; +} + +RSGraphSource::~RSGraphSource() +{ + stop() ; + delete _timer ; +} +void RSGraphSource::clear() +{ + _points.clear() ; +} +void RSGraphSource::stop() +{ + _timer->stop() ; +} +void RSGraphSource::start() +{ + _timer->stop(); + _timer->start((int)(_update_period_msecs)) ; +} + +void RSGraphSource::getDataPoints(int index,std::vector& pts) const +{ + pts.clear() ; + qint64 now = getTime() ; + + for(std::list > >::const_iterator it = _points.begin();it!=_points.end();++it) + pts.push_back(QPointF( (now - (*it).first)/1000.0f,(*it).second[index])) ; +} + +void RSGraphWidget::addSource(RSGraphSource *gs) +{ + _sources.push_back(gs) ; +} + +qint64 RSGraphSource::getTime() const +{ + return QDateTime::currentMSecsSinceEpoch() - _time_orig_msecs ; +} + +void RSGraphSource::update() +{ + std::vector vals ; + getValues(vals) ; + + qint64 ms = getTime() ; + + _points.push_back(std::make_pair(ms,vals)) ; + + //std::cerr << "RSGraphSource::update() Got new data for time " << ms/1000.0 << ", now=" << ms << std::endl; + + // eliminate measurements that stand beyond the time limit + + for(std::list > >::iterator it=_points.begin();it!=_points.end();) + if( ms - (*it).first > _time_limit_msecs) + { + //std::cerr << " removing old value with time " << (*it).first/1000.0f << std::endl; + it = _points.erase(it) ; + } + else + break ; +} + +void RSGraphSource::setCollectionTimeLimit(qint64 s) { _time_limit_msecs = s ; } +void RSGraphSource::setCollectionTimePeriod(qint64 s) { _update_period_msecs = s ; } + +void RSGraphWidget::setTimeScale(float pixels_per_second) +{ + _time_scale =pixels_per_second ; +} + +/** Default contructor */ +RSGraphWidget::RSGraphWidget(QWidget *parent) +: QFrame(parent) +{ + _painter = new QPainter(); + _graphStyle = AreaGraph; + + /* Initialize graph values */ + _maxPoints = getNumPoints(); + _maxValue = MINUSER_SCALE; + + _time_scale = 5.0f ; // in pixels per second. + _timer = new QTimer ; + QObject::connect(_timer,SIGNAL(timeout()),this,SLOT(update())) ; + + _timer->start(1000); +} + +/** Default destructor */ +RSGraphWidget::~RSGraphWidget() +{ + delete _painter; + + for(uint32_t i=0;i<_sources.size();++i) + delete _sources[i] ; +} + +/** Gets the width of the desktop, which is the maximum number of points + * we can plot in the graph. */ +int +RSGraphWidget::getNumPoints() +{ + QDesktopWidget *desktop = QApplication::desktop(); + int width = desktop->width(); + return width; +} + +/** Clears the graph. */ +void +RSGraphWidget::resetGraph() +{ + _maxValue = MINUSER_SCALE; + update(); +} + +/** Toggles display of respective graph lines and counters. */ +//void +//DhtGraph::setShowCounters(bool showRSDHT, bool showALLDHT) +//{ +// _showRSDHT = showRSDHT; +// _showALLDHT = showALLDHT; +// this->update(); +//} + +/** Overloads default QWidget::paintEvent. Draws the actual + * bandwidth graph. */ +void RSGraphWidget::paintEvent(QPaintEvent *) +{ + //std::cerr << "In paint event!" << std::endl; + + /* Set current graph dimensions */ + _rec = this->frameRect(); + + /* Start the painter */ + _painter->begin(this); + + /* We want antialiased lines and text */ + _painter->setRenderHint(QPainter::Antialiasing); + _painter->setRenderHint(QPainter::TextAntialiasing); + + /* Fill in the background */ + _painter->fillRect(_rec, QBrush(BACK_COLOR)); + _painter->drawRect(_rec); + + /* Paint the scale */ + paintScale(); + /* Plot the rsDHT/allDHT data */ + paintData(); + /* Paint the rsDHT/allDHT totals */ + paintTotals(); + + /* Stop the painter */ + _painter->end(); +} + +/** Paints an integral and an outline of that integral for each data set (rsdht + * and/or alldht) that is to be displayed. The integrals will be drawn first, + * followed by the outlines, since we want the area of overlapping integrals + * to blend, but not the outlines of those integrals. */ +void RSGraphWidget::paintData() +{ + /* Convert the bandwidth data points to graph points */ + QVector points ; + pointsFromData(points) ; + + /* Plot the bandwidth data as area graphs */ + if (_graphStyle == AreaGraph) + paintIntegral(points, RSDHT_COLOR, 0.6); + + /* Plot the bandwidth as solid lines. If the graph style is currently an + * area graph, we end up outlining the integrals. */ + paintLine(points, RSDHT_COLOR); +} + +/** Returns a list of points on the bandwidth graph based on the supplied set + * of rsdht or alldht values. */ +void RSGraphWidget::pointsFromData(QVector& points) +{ + points.clear(); + + int x = _rec.width(); + int y = _rec.height(); + qreal scale = (y - (y/10)) / _maxValue; + qreal currValue; + + if(_sources.empty()) + return ; + + const RSGraphSource& source(*_sources.front()) ; + + float time_step = 1.0f ; // number of seconds per pixel + + /* Translate all data points to points on the graph frame */ + + std::vector values ; + + source.getDataPoints(0,values) ; + float last = values.back().x() ; + + //std::cerr << "Got " << values.size() << " values for index 0" << std::endl; + + float last_px = SCALE_WIDTH ; + float last_py = 0.0f ; + + for (int i = 0; i < values.size(); ++i) + { + //std::cerr << "Value: (" << values[i].x() << " , " << values[i].y() << ")" << std::endl; + + // compute point in pixels + + float px = x - (values[i].x()-last)*_time_scale ; + float py = y - values[i].y()*scale ; + + if(px >= SCALE_WIDTH && last_px < SCALE_WIDTH) + { + float alpha = (SCALE_WIDTH - last_px)/(px - last_px) ; + float ipx = SCALE_WIDTH ; + float ipy = (1-alpha)*last_py + alpha*py ; + + points << QPointF(ipx,y) ; + points << QPointF(ipx,ipy) ; + } + else if(i==0) + points << QPointF(px,y) ; + + last_px = px ; + last_py = py ; + + if(px < SCALE_WIDTH) + continue ; + + // remove midle point when 3 consecutive points have the same value. + + if(points.size() > 1 && points[points.size()-2].y() == points.back().y() && points.back().y() == py) + points.pop_back() ; + + points << QPointF(px,py) ; + + if(i==values.size()-1) + points << QPointF(px,y) ; + } +} + +/** Plots an integral using the data points in points. The area will be + * filled in using color and an alpha-blending level of alpha + * (default is opaque). */ +void RSGraphWidget::paintIntegral(const QVector& points, QColor color, qreal alpha) +{ + /* Save the current brush, plot the integral, and restore the old brush */ + QBrush oldBrush = _painter->brush(); + color.setAlphaF(alpha); + _painter->setBrush(QBrush(color)); + _painter->drawPolygon(points.data(), points.size()); + _painter->setBrush(oldBrush); +} + +/** Iterates the input list and draws a line on the graph in the appropriate + * color. */ +void RSGraphWidget::paintLine(const QVector& points, QColor color, Qt::PenStyle lineStyle) +{ + /* Save the current brush, plot the line, and restore the old brush */ + QPen oldPen = _painter->pen(); + _painter->setPen(QPen(color, lineStyle)); + _painter->drawPolyline(points.data(), points.size()); + _painter->setPen(oldPen); +} + +/** Paints selected total indicators on the graph. */ +void RSGraphWidget::paintTotals() +{ + int x = SCALE_WIDTH + FONT_SIZE, y = 0; + int rowHeight = FONT_SIZE; + +#if !defined(Q_WS_MAC) + /* On Mac, we don't need vertical spacing between the text rows. */ + rowHeight += 5; +#endif + +// /* If total received is selected */ +// y = rowHeight; +// _painter->setPen(RSDHT_COLOR); +// _painter->drawText(x, y, +// tr("RetroShare users in DHT: ")+ +// " ("+tr("%1").arg(_rsDHT->first(), 0, 'f', 0)+")"); +} + +/** Returns a formatted string with the correct size suffix. */ +QString RSGraphWidget::totalToStr(qreal total) +{ + /* Determine the correct size suffix */ + if (total < 1024) { + /* Use KB suffix */ + return tr("%1 KB").arg(total, 0, 'f', 2); + } else if (total < 1048576) { + /* Use MB suffix */ + return tr("%1 MB").arg(total/1024.0, 0, 'f', 2); + } else { + /* Use GB suffix */ + return tr("%1 GB").arg(total/1048576.0, 0, 'f', 2); + } +} + +/** Paints the scale on the graph. */ +void RSGraphWidget::paintScale() +{ + qreal markStep = _maxValue * .25; + int top = _rec.y(); + int bottom = _rec.height(); + qreal paintStep = (bottom - (bottom/10)) / 4; + + /* Draw the other marks in their correctly scaled locations */ + qreal scale; + qreal pos; + for (int i = 1; i < 5; i++) { + pos = bottom - (i * paintStep); + scale = i * markStep; + _painter->setPen(SCALE_COLOR); + _painter->drawText(QPointF(5, pos+FONT_SIZE), + tr("%1 Users").arg(scale, 0, 'f', 0)); + _painter->setPen(GRID_COLOR); + _painter->drawLine(QPointF(SCALE_WIDTH, pos), + QPointF(_rec.width(), pos)); + } + + /* Draw vertical separator */ + _painter->drawLine(SCALE_WIDTH, top, SCALE_WIDTH, bottom); +} + diff --git a/retroshare-gui/src/gui/common/RSGraphWidget.h b/retroshare-gui/src/gui/common/RSGraphWidget.h new file mode 100644 index 000000000..111fadd25 --- /dev/null +++ b/retroshare-gui/src/gui/common/RSGraphWidget.h @@ -0,0 +1,177 @@ +/**************************************************************** + * This file is distributed under the following license: + * + * Copyright (C) 2014 RetroShare Team + * Copyright (c) 2006-2007, crypton + * Copyright (c) 2006, Matt Edman, Justin Hipple + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ****************************************************************/ + +#pragma once + +#include +#include +#include + +#include + +#define HOR_SPC 2 /** Space between data points */ +#define SCALE_WIDTH 75 /** Width of the scale */ +#define MINUSER_SCALE 2000 /** 2000 users is the minimum scale */ +#define SCROLL_STEP 4 /** Horizontal change on graph update */ + +#define BACK_COLOR Qt::white +#define SCALE_COLOR Qt::black +#define GRID_COLOR Qt::black +#define RSDHT_COLOR Qt::magenta +#define ALLDHT_COLOR Qt::yellow + +#define FONT_SIZE 11 + +// This class provides a source value that the graph can retrieve on demand. +// In order to use your own source, derive from RSGraphSource and overload the value() method. +// +class RSGraphSource: public QObject +{ + Q_OBJECT + +public: + RSGraphSource(); + virtual ~RSGraphSource() ; + + void start() ; + void stop() ; + void clear() ; + + virtual int n_values() const =0; // Method to overload in your own class + + // Returns the n^th interpolated value at the given time in floating point seconds backward. + virtual void getDataPoints(int index, std::vector& pts) const ; + + // Sets the maximum time for keeping values. Units=seconds. + void setCollectionTimeLimit(qint64 msecs) ; + + // Sets the time period for collecting new values. Units=milliseconds. + void setCollectionTimePeriod(qint64 msecs) ; + +protected slots: + // Calls the internal source for a new data points; called by the timer. + void update() ; + +protected: + virtual void getValues(std::vector& values) const = 0 ; // overload this in your own class to fill in the values you want to display. + + qint64 getTime() const ; // returns time in ms since RS has started + + std::list > > _points ; + + QTimer *_timer ; + + qint64 _time_limit_msecs ; + qint64 _update_period_msecs ; + qint64 _time_orig_msecs ; +}; + +class RSGraphWidget: public QFrame +{ + Q_OBJECT + + public: + static const uint32_t RSGRAPH_FLAGS_AUTO_SCALE_Y = 0x0001 ; // automatically adjust Y scale + static const uint32_t RSGRAPH_FLAGS_LOG_SCALE_Y = 0x0002 ; // log scale in Y + static const uint32_t RSGRAPH_FLAGS_ALWAYS_COLLECT = 0x0004 ; // keep collecting while not displayed + + /** Bandwidth graph style. */ + enum GraphStyle + { + SolidLine = 0, /**< Plot bandwidth as solid lines. */ + AreaGraph = 1 /**< Plot bandwidth as alpha blended area graphs. */ + }; + + /** Default Constructor */ + RSGraphWidget(QWidget *parent = 0); + /** Default Destructor */ + ~RSGraphWidget(); + + // sets the update interval period. + // + void setTimerPeriod(int miliseconds) ; + void addSource(RSGraphSource *gs) ; + void setTimeScale(float pixels_per_second) ; + + /** Add data points. */ + //void addPoints(qreal rsDHT, qreal allDHT); + /** Clears the graph. */ + void resetGraph(); + /** Toggles display of data counters. */ + //void setShowCounters(bool showRSDHT, bool showALLDHT); + /** Sets the graph style used to display bandwidth data. */ + void setGraphStyle(GraphStyle style) { _graphStyle = style; } + + protected: + /** Overloaded QWidget::paintEvent() */ + void paintEvent(QPaintEvent *event); + + protected slots: + void updateDisplay() {} + + private: + /** Gets the width of the desktop, the max # of points. */ + int getNumPoints(); + + /** Paints an integral and an outline of that integral for each data set + * (rsdht and/or alldht) that is to be displayed. */ + void paintData(); + /** Paints the rsdht/alldht totals. */ + void paintTotals(); + /** Paints the scale in the graph. */ + void paintScale(); + /** Returns a formatted string representation of total. */ + QString totalToStr(qreal total); + /** Returns a list of points on the bandwidth graph based on the supplied set + * of rsdht or alldht values. */ + void pointsFromData(QVector & ) ; + + /** Paints a line with the data in points. */ + void paintLine(const QVector& points, QColor color, + Qt::PenStyle lineStyle = Qt::SolidLine); + /** Paints an integral using the supplied data. */ + void paintIntegral(const QVector& points, QColor color, qreal alpha = 1.0); + + /** Style with which the bandwidth data will be graphed. */ + GraphStyle _graphStyle; + /** A QPainter object that handles drawing the various graph elements. */ + QPainter* _painter; + /** The current dimensions of the graph. */ + QRect _rec; + /** The maximum data value plotted. */ + qreal _maxValue; + /** The maximum number of points to store. */ + int _maxPoints; + + qreal _time_scale ; // horizontal scale in pixels per sec. + + /** Show the respective lines and counters. */ + //bool _showRSDHT; + //bool _showALLDHT; + + uint32_t _flags ; + QTimer *_timer ; + + std::vector _sources ; +}; +