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