Vidalia 0.2.15
|
00001 /* 00002 ** This file is part of Vidalia, and is subject to the license terms in the 00003 ** LICENSE file, found in the top level directory of this distribution. If you 00004 ** did not receive the LICENSE file with this file, you may obtain it from the 00005 ** Vidalia source package distributed by the Vidalia Project at 00006 ** http://www.torproject.org/projects/vidalia.html. No part of Vidalia, 00007 ** including this file, may be copied, modified, propagated, or distributed 00008 ** except according to the terms described in the LICENSE file. 00009 */ 00010 00011 /* 00012 ** \file GraphFrame.cpp 00013 ** \brief Graphs a series of send and receive data points 00014 */ 00015 00016 #include "GraphFrame.h" 00017 00018 #include <QtGui> 00019 00020 00021 /** Default contructor */ 00022 GraphFrame::GraphFrame(QWidget *parent) 00023 : QFrame(parent) 00024 { 00025 /* Create Graph Frame related objects */ 00026 _recvData = new QList<qreal>(); 00027 _sendData = new QList<qreal>(); 00028 _painter = new QPainter(); 00029 _graphStyle = SolidLine; 00030 00031 /* Initialize graph values */ 00032 _recvData->prepend(0); 00033 _sendData->prepend(0); 00034 _maxPoints = getNumPoints(); 00035 _maxPosition = 0; 00036 _showRecv = true; 00037 _showSend = true; 00038 _maxValue = MIN_SCALE; 00039 _scaleWidth = 0; 00040 } 00041 00042 /** Default destructor */ 00043 GraphFrame::~GraphFrame() 00044 { 00045 delete _painter; 00046 delete _recvData; 00047 delete _sendData; 00048 } 00049 00050 /** Gets the width of the desktop, which is the maximum number of points 00051 * we can plot in the graph. */ 00052 int 00053 GraphFrame::getNumPoints() 00054 { 00055 return size().width() - _scaleWidth; 00056 } 00057 00058 /** Adds new data points to the graph. */ 00059 void 00060 GraphFrame::addPoints(qreal recv, qreal send) 00061 { 00062 /* If maximum number of points plotted, remove oldest */ 00063 if (_sendData->size() == _maxPoints) { 00064 _sendData->removeLast(); 00065 _recvData->removeLast(); 00066 } 00067 00068 /* Update the displayed maximum */ 00069 if (_maxPosition >= _maxPoints) { 00070 _maxValue = MIN_SCALE; 00071 foreach(qreal send, *_sendData) 00072 if(send > _maxValue) 00073 _maxValue = send; 00074 foreach(qreal recv, *_recvData) 00075 if(recv > _maxValue) 00076 _maxValue = recv; 00077 _maxPosition = 0; 00078 } 00079 00080 /* Add the points to their respective lists */ 00081 _sendData->prepend(send); 00082 _recvData->prepend(recv); 00083 00084 /* Add to the total counters */ 00085 _totalSend += send; 00086 _totalRecv += recv; 00087 00088 bool maxUpdated = false; 00089 /* Check for a new maximum value */ 00090 if (send > _maxValue) { 00091 _maxValue = send; 00092 maxUpdated = true; 00093 } 00094 00095 if (recv > _maxValue) { 00096 _maxValue = recv; 00097 maxUpdated = true; 00098 } 00099 00100 if (maxUpdated) { 00101 _maxPosition = 0; 00102 } else { 00103 _maxPosition++; 00104 } 00105 00106 this->update(); 00107 } 00108 00109 /** Clears the graph. */ 00110 void 00111 GraphFrame::resetGraph() 00112 { 00113 _recvData->clear(); 00114 _sendData->clear(); 00115 _recvData->prepend(0); 00116 _sendData->prepend(0); 00117 _maxValue = MIN_SCALE; 00118 _totalSend = 0; 00119 _totalRecv = 0; 00120 this->update(); 00121 } 00122 00123 /** Toggles display of respective graph lines and counters. */ 00124 void 00125 GraphFrame::setShowCounters(bool showRecv, bool showSend) 00126 { 00127 _showRecv = showRecv; 00128 _showSend = showSend; 00129 this->update(); 00130 } 00131 00132 /** Overloads default QWidget::paintEvent. Draws the actual 00133 * bandwidth graph. */ 00134 void 00135 GraphFrame::paintEvent(QPaintEvent *event) 00136 { 00137 Q_UNUSED(event); 00138 00139 /* Set current graph dimensions */ 00140 _rec = this->frameRect(); 00141 00142 /* Start the painter */ 00143 _painter->begin(this); 00144 00145 /* We want antialiased lines and text */ 00146 _painter->setRenderHint(QPainter::Antialiasing); 00147 _painter->setRenderHint(QPainter::TextAntialiasing); 00148 00149 /* Fill in the background */ 00150 _painter->fillRect(_rec, QBrush(BACK_COLOR)); 00151 _painter->drawRect(_rec); 00152 00153 /* Paint the scale */ 00154 paintScale(); 00155 /* Plot the send/receive data */ 00156 paintData(); 00157 /* Paint the send/recv totals */ 00158 paintTotals(); 00159 00160 /* Stop the painter */ 00161 _painter->end(); 00162 } 00163 00164 /** Paints an integral and an outline of that integral for each data set (send 00165 * and/or receive) that is to be displayed. The integrals will be drawn first, 00166 * followed by the outlines, since we want the area of overlapping integrals 00167 * to blend, but not the outlines of those integrals. */ 00168 void 00169 GraphFrame::paintData() 00170 { 00171 QVector<QPointF> recvPoints, sendPoints; 00172 00173 /* Convert the bandwidth data points to graph points */ 00174 recvPoints = pointsFromData(_recvData); 00175 sendPoints = pointsFromData(_sendData); 00176 00177 if (_graphStyle == AreaGraph) { 00178 /* Plot the bandwidth data as area graphs */ 00179 if (_showRecv) 00180 paintIntegral(recvPoints, RECV_COLOR, 0.6); 00181 if (_showSend) 00182 paintIntegral(sendPoints, SEND_COLOR, 0.4); 00183 } 00184 00185 /* Plot the bandwidth as solid lines. If the graph style is currently an 00186 * area graph, we end up outlining the integrals. */ 00187 if (_showRecv) 00188 paintLine(recvPoints, RECV_COLOR); 00189 if (_showSend) 00190 paintLine(sendPoints, SEND_COLOR); 00191 } 00192 00193 /** Returns a list of points on the bandwidth graph based on the supplied set 00194 * of send or receive values. */ 00195 QVector<QPointF> 00196 GraphFrame::pointsFromData(QList<qreal>* list) 00197 { 00198 QVector<QPointF> points; 00199 int x = _rec.width(); 00200 int y = _rec.height(); 00201 qreal scale = (y - (y/10)) / _maxValue; 00202 qreal currValue; 00203 00204 /* Translate all data points to points on the graph frame */ 00205 points << QPointF(x, y); 00206 for (int i = 0; i < list->size(); i++) { 00207 currValue = y - (list->at(i) * scale); 00208 if (x - SCROLL_STEP < _scaleWidth) { 00209 points << QPointF(_scaleWidth, currValue); 00210 break; 00211 } 00212 points << QPointF(x, currValue); 00213 x -= SCROLL_STEP; 00214 } 00215 points << QPointF(_scaleWidth, y); 00216 return points; 00217 } 00218 00219 /** Plots an integral using the data points in <b>points</b>. The area will be 00220 * filled in using <b>color</b> and an alpha-blending level of <b>alpha</b> 00221 * (default is opaque). */ 00222 void 00223 GraphFrame::paintIntegral(QVector<QPointF> points, QColor color, qreal alpha) 00224 { 00225 /* Save the current brush, plot the integral, and restore the old brush */ 00226 QBrush oldBrush = _painter->brush(); 00227 color.setAlphaF(alpha); 00228 _painter->setBrush(QBrush(color)); 00229 _painter->drawPolygon(points.data(), points.size()); 00230 _painter->setBrush(oldBrush); 00231 } 00232 00233 /** Iterates the input list and draws a line on the graph in the appropriate 00234 * color. */ 00235 void 00236 GraphFrame::paintLine(QVector<QPointF> points, QColor color, Qt::PenStyle lineStyle) 00237 { 00238 /* Save the current brush, plot the line, and restore the old brush */ 00239 QPen oldPen = _painter->pen(); 00240 _painter->setPen(QPen(color, lineStyle)); 00241 _painter->drawPolyline(points.data(), points.size()); 00242 _painter->setPen(oldPen); 00243 } 00244 00245 /** Paints selected total indicators on the graph. */ 00246 void 00247 GraphFrame::paintTotals() 00248 { 00249 int x = _scaleWidth + FONT_SIZE, y = 0; 00250 int rowHeight = FONT_SIZE; 00251 00252 #if !defined(Q_WS_MAC) 00253 /* On Mac, we don't need vertical spacing between the text rows. */ 00254 rowHeight += 5; 00255 #endif 00256 00257 /* If total received is selected */ 00258 if (_showRecv) { 00259 y = rowHeight; 00260 _painter->setPen(RECV_COLOR); 00261 _painter->drawText(x, y, 00262 tr("Recv: ") + totalToStr(_totalRecv) + 00263 " ("+tr("%1 KB/s").arg(_recvData->first(), 0, 'f', 2)+")"); 00264 } 00265 00266 /* If total sent is selected */ 00267 if (_showSend) { 00268 y += rowHeight; 00269 _painter->setPen(SEND_COLOR); 00270 _painter->drawText(x, y, 00271 tr("Sent: ") + totalToStr(_totalSend) + 00272 " ("+tr("%1 KB/s").arg(_sendData->first(), 0, 'f', 2)+")"); 00273 } 00274 } 00275 00276 /** Returns a formatted string with the correct size suffix. */ 00277 QString 00278 GraphFrame::totalToStr(qreal total) 00279 { 00280 /* Determine the correct size suffix */ 00281 if (total < 1024) { 00282 /* Use KB suffix */ 00283 return tr("%1 KB").arg(total, 0, 'f', 2); 00284 } else if (total < 1048576) { 00285 /* Use MB suffix */ 00286 return tr("%1 MB").arg(total/1024.0, 0, 'f', 2); 00287 } else { 00288 /* Use GB suffix */ 00289 return tr("%1 GB").arg(total/1048576.0, 0, 'f', 2); 00290 } 00291 } 00292 00293 /** Returns the width in pixels of <b>label</b> using the current painter's 00294 * font. */ 00295 int 00296 GraphFrame::labelWidth(const QString &label) 00297 { 00298 int width = 0; 00299 QFontMetrics fm = fontMetrics(); 00300 00301 for (int i = 0; i < label.length(); i++) 00302 width += fm.charWidth(label, i); 00303 return width; 00304 } 00305 00306 /** Paints the scale on the graph. */ 00307 void 00308 GraphFrame::paintScale() 00309 { 00310 QString label[4]; 00311 int width[4]; 00312 int top = _rec.y(); 00313 int bottom = _rec.height(); 00314 int scaleWidth = 0; 00315 qreal pos; 00316 qreal markStep = _maxValue * .25; 00317 qreal paintStep = (bottom - (bottom/8)) / 4; 00318 00319 /* Compute each of the y-axis labels */ 00320 for (int i = 0; i < 4; i++) { 00321 pos = bottom - ((i+1) * paintStep); 00322 label[i] = tr("%1 KB/s").arg(markStep*(i+1), 0, 'f', 2); 00323 width[i] = labelWidth(label[i]); 00324 scaleWidth = qMax(scaleWidth, 2+width[i]); 00325 } 00326 00327 /* Include a 5px margin between the y-axis and its labels */ 00328 _scaleWidth = scaleWidth + 5; 00329 00330 /* Draw the y-axis labels and horizontal marks in their correctly scaled 00331 * locations */ 00332 for (int i = 0; i < 4; i++) { 00333 pos = bottom - ((i+1) * paintStep); 00334 _painter->setPen(SCALE_COLOR); 00335 _painter->drawText(QPointF(_scaleWidth-width[i]-5, pos), label[i]); 00336 00337 _painter->setPen(GRID_COLOR); 00338 _painter->drawLine(QPointF(_scaleWidth, pos), 00339 QPointF(_rec.width(), pos)); 00340 } 00341 00342 /* Draw the y-axis */ 00343 _painter->drawLine(_scaleWidth, top, _scaleWidth, bottom); 00344 } 00345 00346 void 00347 GraphFrame::resizeEvent(QResizeEvent *ev) 00348 { 00349 _maxPoints = ev->size().width() - _scaleWidth; 00350 _maxPoints /= SCROLL_STEP; 00351 }