如下是私有函數的實現:
void Plotter::updateRubberBandRegion()
{
QRect rect = rubberBandRect.normalized();
update(rect.left(), rect.top(), rect.width(), 1);
update(rect.left(), rect.top(), 1, rect.height());
update(rect.left(), rect.bottom(), rect.width(), 1);
update(rect.right(), rect.top(), 1, rect.height());
}
函數updateRubberBand()在mousePressEvent(),mouseMoveEvent()和mouseReleaseEvent()中被調用,用來刪除或者從新繪製橡皮線。函數中調用了四次update(),用四個繪製事件完成由橡皮線(兩條垂直和水平的線)組成的四個小矩形的繪製。Qt也提供了一個類QRubberBand用來繪製橡皮線,可是控件本身提供的繪製函數會更好
void Plotter::refreshPixmap()
{
pixmap = QPixmap(size());
pixmap.fill(this, 0, 0);
QPainter painter(&pixmap);
painter.initFrom(this);
drawGrid(&painter);
drawCurves(&painter);
update();
}
函數refreshPixmap()把plot繪製到圖片上,而且更新顯示。首先咱們把圖片的大小調整爲和當前控件大小相同,而後用控件的背景顏色填充整個圖片。這個顏色是當前調色版的「dark」部分,由於在Plotter構造函數中調用setBackgroundRole() 。若是背景用的刷子是非實心的(solid brush,刷子的樣式,只有顏色,沒有花紋的那種最簡單的),QPixmap::fill()須要知道控件中刷子的偏移量,以便圖片對齊刷子模式。這裏圖片對應整個控件,所以偏移位置爲(0,0)。
接下來咱們建立了一個QPainter對象來繪製圖片,QPainter::initFrom()設置繪製圖片所需畫筆,背景和字體,參數this表示這些設置和Plotter控件的相應設置是一致的。而後咱們調用drawGrid(),drawCurves()繪製網格和曲線。最後,update()函數安排整個控件的繪製事件,在painteEvent()函數中把圖片拷貝到控件上。
void Plotter::drawGrid(QPainter *painter)
{
QRect rect(Margin, Margin,
width() - 2 * Margin, height() - 2 * Margin);
if (!rect.isValid())
return;
PlotSettings settings = zoomStack[curZoom];
QPen quiteDark = palette().dark().color().light();
QPen light = palette().light().color();
for (int i = 0; i <= settings.numXTicks; ++i) {
int x = rect.left() + (i * (rect.width() - 1)
/ settings.numXTicks);
double label = settings.minX + (i * settings.spanX()
/ settings.numXTicks);
painter->setPen(quiteDark);
painter->drawLine(x, rect.top(), x, rect.bottom());
painter->setPen(light);
painter->drawLine(x, rect.bottom(), x, rect.bottom() + 5);
painter->drawText(x - 50, rect.bottom() + 5, 100, 15,
Qt::AlignHCenter | Qt::AlignTop,
QString::number(label));
}
for (int j = 0; j <= settings.numYTicks; ++j) {
int y = rect.bottom() - (j * (rect.height() - 1)
/ settings.numYTicks);
double label = settings.minY + (j * settings.spanY()
/ settings.numYTicks);
painter->setPen(quiteDark);
painter->drawLine(rect.left(), y, rect.right(), y);
painter->setPen(light);
painter->drawLine(rect.left() - 5, y, rect.left(), y);
painter->drawText(rect.left() - Margin, y - 10, Margin - 5, 20,
Qt::AlignRight | Qt::AlignVCenter,
QString::number(label));
}
painter->drawRect(rect.adjusted(0, 0, -1, -1));
}
函數drawGrid()在座標軸和曲線的下面繪製網格。這個區域由一個矩形肯定,若是控件過小,則不繪製當即返回。第一個循環繪製網格的垂直線,及沿x座標軸的刻度。第二個循環繪製網格的水平線,及沿y座標軸的刻度。最後,沿邊界繪製一個矩形。drawText()繪製數字,對應兩個座標軸上刻度的標記。
函數painter->drawText()語法以下:
painter->drawText(x, y, width, height, alignment, text);
其中(x,y,width,height)所肯定的矩形,alignment肯定文字在矩形中的位置。
void Plotter::drawCurves(QPainter *painter)
{
static const QColor colorForIds[6] = {
Qt::red, Qt::green, Qt::blue, Qt::cyan, Qt::magenta, Qt::yellow
};
PlotSettings settings = zoomStack[curZoom];
QRect rect(Margin, Margin,
width() - 2 * Margin, height() - 2 * Margin);
if (!rect.isValid())
return;
painter->setClipRect(rect.adjusted(+1, +1, -1, -1));
QMapIterator<int, QVector<QPointF> > i(curveMap);
while (i.hasNext()) {
i.next();
int id = i.key();
const QVector<QPointF> &data = i.value();
QPolygonF polyline(data.count());
for (int j = 0; j < data.count(); ++j) {
double dx = data[j].x() - settings.minX;
double dy = data[j].y() - settings.minY;
double x = rect.left() + (dx * (rect.width() - 1)
/ settings.spanX());
double y = rect.bottom() - (dy * (rect.height() - 1)
/ settings.spanY());
polyline[j] = QPointF(x, y);
}
painter->setPen(colorForIds[uint(id) % 6]);
painter->drawPolyline(polyline);
}
}
函數drawCurves()在網格的上層繪製曲線。調用了QPainter::setClipRect()函數設置QPainter的剪切區域爲包含曲線的矩形區域(不包括四周的間隙和圖片的外框)。QPainter會忽略這個區域外的象素。
而後咱們使用Java風格的迭代器,遍歷全部的曲線,對每一條曲線,遍歷它全部的QPointF點。函數key()獲得曲線的id,value()函數獲得曲線的QVector<QPointF>類型的數據。內層循環把每一個QPointF記錄的plotter座標轉換爲控件座標,把結果保存在polyline變量中。