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