双缓冲技术(Double Buffering)( 私有函数的实现)
以下是私有函数的实现:
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”部分。如果背景用的刷子不是固体的(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 会忽略画到这个区域外的象素。然后我们遍历所有的曲线,在每一条曲线,遍历它所有的 QPointF 点。函数 key()得到曲线的id, value()函数得到曲线的QVector<QPointF>类型的数据。内层循环把QPointF 记录的plotter坐标转换为控件坐标,把它们保存在多段线变量中。转换坐标后,我们设置画笔的颜色(使用函数前面预定义的颜色),调用 drawPolyline()绘制出所有的曲线的点。