5-4 雙緩衝技術(Double Buffering)(1、簡介和原始碼部分)
阿新 • • 發佈:2019-02-10
#include <QtGui>
#include <cmath>
#include "plotter.h"
Plotter::Plotter(QWidget *parent)
: QWidget(parent)
{
setBackgroundRole(QPalette::Dark);
setAutoFillBackground(true);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
setFocusPolicy(Qt::StrongFocus);
rubberBandIsShown =false;
zoomInButton =new QToolButton(this);
zoomInButton->setIcon(QIcon(":/images/zoomin.png"));
zoomInButton->adjustSize();
connect(zoomInButton, SIGNAL(clicked()), this, SLOT(zoomIn()));
zoomOutButton =new QToolButton(this);
zoomOutButton->setIcon(QIcon(":/images/zoomout.png "));
zoomOutButton->adjustSize();
connect(zoomOutButton, SIGNAL(clicked()), this, SLOT(zoomOut()));
setPlotSettings(PlotSettings());
}
void Plotter::setPlotSettings(const PlotSettings &settings)
{
zoomStack.clear();
zoomStack.append(settings);
curZoom =0;
zoomInButton->hide();
zoomOutButton ->hide();
refreshPixmap();
}
void Plotter::zoomOut()
{
if (curZoom >0) {
--curZoom;
zoomOutButton->setEnabled(curZoom >0);
zoomInButton->setEnabled(true);
zoomInButton->show();
refreshPixmap();
}
}
void Plotter::zoomIn()
{
if (curZoom < zoomStack.count() -1) {
++curZoom;
zoomInButton->setEnabled(curZoom < zoomStack.count() -1);
zoomOutButton->setEnabled(true);
zoomOutButton->show();
refreshPixmap();
}
}
void Plotter::setCurveData(int id, const QVector<QPointF>&data)
{
curveMap[id] = data;
refreshPixmap();
}
void Plotter::clearCurve(int id)
{
curveMap.remove(id);
refreshPixmap();
}
QSize Plotter::minimumSizeHint() const
{
return QSize(6* Margin, 4* Margin);
}
QSize Plotter::sizeHint() const
{
return QSize(12* Margin, 8* Margin);
}
void Plotter::paintEvent(QPaintEvent */* event */)
{
QStylePainter painter(this);
painter.drawPixmap(0, 0, pixmap);
if (rubberBandIsShown) {
painter.setPen(palette().light().color());
painter.drawRect(rubberBandRect.normalized()
.adjusted(0, 0, -1, -1));
}
if (hasFocus()) {
QStyleOptionFocusRect option;
option.initFrom(this);
option.backgroundColor = palette().dark().color();
painter.drawPrimitive(QStyle::PE_FrameFocusRect, option);
}
}
void Plotter::resizeEvent(QResizeEvent */* event */)
{
int x = width() - (zoomInButton->width()
+ zoomOutButton->width() +10);
zoomInButton->move(x, 5);
zoomOutButton->move(x + zoomInButton->width() +5, 5);
refreshPixmap();
}
void Plotter::resizeEvent(QResizeEvent */* event */)
{
int x = width() - (zoomInButton->width()
+ zoomOutButton->width() +10);
zoomInButton->move(x, 5);
zoomOutButton->move(x + zoomInButton->width() +5, 5);
refreshPixmap();
}
void Plotter::resizeEvent(QResizeEvent */* event */)
{
int x = width() - (zoomInButton->width()
+ zoomOutButton->width() +10);
zoomInButton->move(x, 5);
zoomOutButton->move(x + zoomInButton->width() +5, 5);
refreshPixmap();
}
void Plotter::mousePressEvent(QMouseEvent *event)
{
QRect rect(Margin, Margin,
width() -2* Margin, height() -2* Margin);
if (event->button() == Qt::LeftButton) {
if (rect.contains(event->pos())) {
rubberBandIsShown =true;
rubberBandRect.setTopLeft(event->pos());
rubberBandRect.setBottomRight(event->pos());
updateRubberBandRegion();
setCursor(Qt::CrossCursor);
}
}
}
void Plotter::mouseMoveEvent(QMouseEvent *event)
{
if (rubberBandIsShown) {
updateRubberBandRegion();
rubberBandRect.setBottomRight(event->pos());
updateRubberBandRegion();
}
}
void Plotter::mouseReleaseEvent(QMouseEvent *event)
{
if ((event->button() == Qt::LeftButton) && rubberBandIsShown) {
rubberBandIsShown =false;
updateRubberBandRegion();
unsetCursor();
QRect rect = rubberBandRect.normalized();
if (rect.width() <4|| rect.height() <4)
return;
rect.translate(-Margin, -Margin);
PlotSettings prevSettings = zoomStack[curZoom];
PlotSettings settings;
double dx = prevSettings.spanX() / (width() -2* Margin);
double dy = prevSettings.spanY() / (height() -2* Margin);
settings.minX = prevSettings.minX + dx * rect.left();
settings.maxX = prevSettings.minX + dx * rect.right();
settings.minY = prevSettings.maxY - dy * rect.bottom();
settings.maxY = prevSettings.maxY - dy * rect.top();
settings.adjust();
zoomStack.resize(curZoom +1);
zoomStack.append(settings);
zoomIn();
}
}
void Plotter::keyPressEvent(QKeyEvent *event)
{
switch (event->key()) {
case Qt::Key_Plus:
zoomIn();
break;
case Qt::Key_Minus:
zoomOut();
break;
case Qt::Key_Left:
zoomStack[curZoom].scroll(-1, 0);
refreshPixmap();
break;
case Qt::Key_Right:
zoomStack[curZoom].scroll(+1, 0);
refreshPixmap();
break;
case Qt::Key_Down:
zoomStack[curZoom].scroll(0, -1);
refreshPixmap();
break;
case Qt::Key_Up:
zoomStack[curZoom].scroll(0, +1);
refreshPixmap();
break;
default:
QWidget::keyPressEvent(event);
}
}
void Plotter::wheelEvent(QWheelEvent *event)
{
int numDegrees =event->delta() /8;
int numTicks = numDegrees /15;
if (event->orientation() == Qt::Horizontal) {
zoomStack[curZoom].scroll(numTicks, 0);
} else {
zoomStack[curZoom].scroll(0, numTicks);
}
refreshPixmap();
}
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());
}
void Plotter::refreshPixmap()
{
pixmap = QPixmap(size());
pixmap.fill(this, 0, 0);
QPainter painter(&pixmap);
painter.initFrom(this);
drawGrid(&painter);
drawCurves(&painter);
update();
}
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));
}
void Plotter::drawCurves(QPainter *painter)
{
staticconst 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);
}
}
////////////////////////////////////////////////////////////
PlotSettings::PlotSettings()
{
minX =0.0;
maxX =10.0;
numXTicks =5;
minY =0.0;
maxY =10.0;
numYTicks =5;
}
void PlotSettings::scroll(int dx, int dy)
{
double stepX = spanX() / numXTicks;
minX += dx * stepX;
maxX += dx * stepX;
double stepY = spanY() / numYTicks;
minY += dy * stepY;
maxY += dy * stepY;
}
void PlotSettings::adjust()
{
adjustAxis(minX, maxX, numXTicks);
adjustAxis(minY, maxY, numYTicks);
}
void PlotSettings::adjustAxis(double&min, double&max,
int&numTicks)
{
constint MinTicks =4;
double grossStep = (max - min) / MinTicks;
double step = pow(10.0, floor(log10(grossStep)));
if (5* step < grossStep) {
step *=5;
} elseif (2* step < grossStep) {
step *=2;
}
numTicks =int(ceil(max / step) - floor(min / step));
if (numTicks < MinTicks)
numTicks = MinTicks;
min = floor(min / step) * step;
max = ceil(max / step) * step;
}
#include <cmath>
#include "plotter.h"
Plotter::Plotter(QWidget *parent)
: QWidget(parent)
{
setBackgroundRole(QPalette::Dark);
setAutoFillBackground(true);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
setFocusPolicy(Qt::StrongFocus);
rubberBandIsShown
zoomInButton =new QToolButton(this);
zoomInButton->setIcon(QIcon(":/images/zoomin.png"));
zoomInButton->adjustSize();
connect(zoomInButton, SIGNAL(clicked()), this, SLOT(zoomIn()));
zoomOutButton =new QToolButton(this);
zoomOutButton->setIcon(QIcon(":/images/zoomout.png
zoomOutButton->adjustSize();
connect(zoomOutButton, SIGNAL(clicked()), this, SLOT(zoomOut()));
setPlotSettings(PlotSettings());
}
void Plotter::setPlotSettings(const PlotSettings &settings)
{
zoomStack.clear();
zoomStack.append(settings);
curZoom =0;
zoomInButton->hide();
zoomOutButton
refreshPixmap();
}
void Plotter::zoomOut()
{
if (curZoom >0) {
--curZoom;
zoomOutButton->setEnabled(curZoom >0);
zoomInButton->setEnabled(true);
zoomInButton->show();
refreshPixmap();
}
}
void Plotter::zoomIn()
{
if (curZoom < zoomStack.count() -1) {
++curZoom;
zoomInButton->setEnabled(curZoom < zoomStack.count() -1);
zoomOutButton->setEnabled(true);
zoomOutButton->show();
refreshPixmap();
}
}
void Plotter::setCurveData(int id, const QVector<QPointF>&data)
{
curveMap[id] = data;
refreshPixmap();
}
void Plotter::clearCurve(int id)
{
curveMap.remove(id);
refreshPixmap();
}
QSize Plotter::minimumSizeHint() const
{
return QSize(6* Margin, 4* Margin);
}
QSize Plotter::sizeHint() const
{
return QSize(12* Margin, 8* Margin);
}
void Plotter::paintEvent(QPaintEvent */* event */)
{
QStylePainter painter(this);
painter.drawPixmap(0, 0, pixmap);
if (rubberBandIsShown) {
painter.setPen(palette().light().color());
painter.drawRect(rubberBandRect.normalized()
.adjusted(0, 0, -1, -1));
}
if (hasFocus()) {
QStyleOptionFocusRect option;
option.initFrom(this);
option.backgroundColor = palette().dark().color();
painter.drawPrimitive(QStyle::PE_FrameFocusRect, option);
}
}
void Plotter::resizeEvent(QResizeEvent */* event */)
{
int x = width() - (zoomInButton->width()
+ zoomOutButton->width() +10);
zoomInButton->move(x, 5);
zoomOutButton->move(x + zoomInButton->width() +5, 5);
refreshPixmap();
}
void Plotter::resizeEvent(QResizeEvent */* event */)
{
int x = width() - (zoomInButton->width()
+ zoomOutButton->width() +10);
zoomInButton->move(x, 5);
zoomOutButton->move(x + zoomInButton->width() +5, 5);
refreshPixmap();
}
void Plotter::resizeEvent(QResizeEvent */* event */)
{
int x = width() - (zoomInButton->width()
+ zoomOutButton->width() +10);
zoomInButton->move(x, 5);
zoomOutButton->move(x + zoomInButton->width() +5, 5);
refreshPixmap();
}
void Plotter::mousePressEvent(QMouseEvent *event)
{
QRect rect(Margin, Margin,
width() -2* Margin, height() -2* Margin);
if (event->button() == Qt::LeftButton) {
if (rect.contains(event->pos())) {
rubberBandIsShown =true;
rubberBandRect.setTopLeft(event->pos());
rubberBandRect.setBottomRight(event->pos());
updateRubberBandRegion();
setCursor(Qt::CrossCursor);
}
}
}
void Plotter::mouseMoveEvent(QMouseEvent *event)
{
if (rubberBandIsShown) {
updateRubberBandRegion();
rubberBandRect.setBottomRight(event->pos());
updateRubberBandRegion();
}
}
void Plotter::mouseReleaseEvent(QMouseEvent *event)
{
if ((event->button() == Qt::LeftButton) && rubberBandIsShown) {
rubberBandIsShown =false;
updateRubberBandRegion();
unsetCursor();
QRect rect = rubberBandRect.normalized();
if (rect.width() <4|| rect.height() <4)
return;
rect.translate(-Margin, -Margin);
PlotSettings prevSettings = zoomStack[curZoom];
PlotSettings settings;
double dx = prevSettings.spanX() / (width() -2* Margin);
double dy = prevSettings.spanY() / (height() -2* Margin);
settings.minX = prevSettings.minX + dx * rect.left();
settings.maxX = prevSettings.minX + dx * rect.right();
settings.minY = prevSettings.maxY - dy * rect.bottom();
settings.maxY = prevSettings.maxY - dy * rect.top();
settings.adjust();
zoomStack.resize(curZoom +1);
zoomStack.append(settings);
zoomIn();
}
}
void Plotter::keyPressEvent(QKeyEvent *event)
{
switch (event->key()) {
case Qt::Key_Plus:
zoomIn();
break;
case Qt::Key_Minus:
zoomOut();
break;
case Qt::Key_Left:
zoomStack[curZoom].scroll(-1, 0);
refreshPixmap();
break;
case Qt::Key_Right:
zoomStack[curZoom].scroll(+1, 0);
refreshPixmap();
break;
case Qt::Key_Down:
zoomStack[curZoom].scroll(0, -1);
refreshPixmap();
break;
case Qt::Key_Up:
zoomStack[curZoom].scroll(0, +1);
refreshPixmap();
break;
default:
QWidget::keyPressEvent(event);
}
}
void Plotter::wheelEvent(QWheelEvent *event)
{
int numDegrees =event->delta() /8;
int numTicks = numDegrees /15;
if (event->orientation() == Qt::Horizontal) {
zoomStack[curZoom].scroll(numTicks, 0);
} else {
zoomStack[curZoom].scroll(0, numTicks);
}
refreshPixmap();
}
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());
}
void Plotter::refreshPixmap()
{
pixmap = QPixmap(size());
pixmap.fill(this, 0, 0);
QPainter painter(&pixmap);
painter.initFrom(this);
drawGrid(&painter);
drawCurves(&painter);
update();
}
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));
}
void Plotter::drawCurves(QPainter *painter)
{
staticconst 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);
}
}
////////////////////////////////////////////////////////////
PlotSettings::PlotSettings()
{
minX =0.0;
maxX =10.0;
numXTicks =5;
minY =0.0;
maxY =10.0;
numYTicks =5;
}
void PlotSettings::scroll(int dx, int dy)
{
double stepX = spanX() / numXTicks;
minX += dx * stepX;
maxX += dx * stepX;
double stepY = spanY() / numYTicks;
minY += dy * stepY;
maxY += dy * stepY;
}
void PlotSettings::adjust()
{
adjustAxis(minX, maxX, numXTicks);
adjustAxis(minY, maxY, numYTicks);
}
void PlotSettings::adjustAxis(double&min, double&max,
int&numTicks)
{
constint MinTicks =4;
double grossStep = (max - min) / MinTicks;
double step = pow(10.0, floor(log10(grossStep)));
if (5* step < grossStep) {
step *=5;
} elseif (2* step < grossStep) {
step *=2;
}
numTicks =int(ceil(max / step) - floor(min / step));
if (numTicks < MinTicks)
numTicks = MinTicks;
min = floor(min / step) * step;
max = ceil(max / step) * step;
}