(转)双缓冲技术1
标签: qtplotsignalvariablesscroll编程
2008-05-20 20:32
1960人阅读
收藏
举报
本文章已收录于:
分类:
Qt(31)
作者同类文章
X
•(转)利用Qt的qmake创建vc工程 •(转)QT事件机制 •(转)QT中画图方法的不同 •(转)5.4 双缓冲技术(Double Buffering)-5 •(转)5.4 双缓冲技术(Double Buffering)-4 更多
Rubber band(橡皮筋线,或者橡皮线), pixmap(图像,双缓冲中用到的图像,有时也直呼pixmap),off-screen pixmap(离线图像)
Plot(plot,这一节实现的就是一个绘制曲线的控件Plotter,有时原文也叫plot,有点小名的意思,没有翻译,直接呼之)
废话少说,以下是译文:
双缓冲技术是GUI编程中常用的技术。所谓的双缓冲就是把把一个需要渲染的控件保存到一个离线图像(off-screen pixmap)中,然后再把图像拷贝到需要绘制的控件上。在Qt的早期版本中,为了用户界面更加清爽,经常用这个技术来消除闪烁。
在Qt4中,QWidget能够自动处理闪烁,因此我们不用再担心这个问题。尽管如此,如果控件渲染复杂且需要经常刷新,双缓冲技术还是很有用的。我们可以把控件永久保存在一个图像中,随时准备下一次绘制事件的到来,一旦接到一个控件的绘制事件,就把图片拷贝到控件上。如果我们要做的只是小范围的修改,这个技术更是尤为有用,如要绘制一条橡皮筋线,就不必一次次刷新整个控件了。
在本章的最后一节,我们实现的是一个叫做Plotter的自定义控件。这个控件使用了双缓冲技术,也涉及到了Qt编程的其他方面:如键盘的事件处理,布局和坐标系统。
Plotter控件用来显示一条或者多条曲线,这些曲线由一组向量坐标表示。用户可以在图像上画一个橡皮筋线(Rubeber band),Plotter控件对橡皮筋线包围的区域进行放大。用户用鼠标左键在控件上选择一个点,然后拖动鼠标走到另一点,然后释放鼠标,就在控件上绘制一条橡皮筋线。
Figure 5.7 Zooming in on the Plotter Widget
用户可以多次用橡皮筋线进行放大,也可以用ZoomOut按钮缩小,然后用ZoomIn按钮再放大。ZoomOut和ZoomIn按钮只是在控件第一次放大或者缩小操作后变得可见,如果用户不缩放图形,则这两个按钮会一直不可见,这样可以使绘图区域不那么混乱。
Plotter控件可以存储任何数量的曲线的数据。同时它还维护一个PlotSettings对象的栈区域,每一个PlotSettings对象都是对应一个特定的缩放值。
首先看一下头文件的代码(对头文件的解析在代码中用注释的形式给出):
#ifndef PLOTTER_H
#define
PLOTTER_H
#include
<
QMap
>
//
包含的Qt的头文件
#include
<
QPixmap
>
#include
<
QVector
>
#include
<
QWidget
>
class
QToolButton;
//
两个前向声明
class
PlotSettings;
class
Plotter :
public
QWidget { Q_OBJECT
public
: Plotter(QWidget
*
parent
=
0
);
void
setPlotSettings(
const
PlotSettings
&
settings);
void
setCurveData(
int
id,
const
QVector
<
QPointF
>
&
data);//QPointF表明是浮点类型的QPoint
void
clearCurve(
int
id); QSize minimumSizeHint()
const
;
//
重写QWidget::minimumSizeHint()
QSize sizeHint()
const
;
//
重写QWidget::sizeHint()
public
slots:
void
zoomIn();
//
放大曲线
void
zoomOut();
//
缩小显示曲线
protected
:
//
重新实现的QWidget的事件处理函数
void
paintEvent(QPaintEvent
*
event
);
void
resizeEvent(QResizeEvent
*
event
);
void
mousePressEvent(QMouseEvent
*
event
);
void
mouseMoveEvent(QMouseEvent
*
event
);
void
mouseReleaseEvent(QMouseEvent
*
event
);
void
keyPressEvent(QKeyEvent
*
event
);
void
wheelEvent(QWheelEvent
*
event
);
private
:
void
updateRubberBandRegion();
void
refreshPixmap();
void
drawGrid(QPainter
*
painter);
void
drawCurves(QPainter
*
painter);
enum
{ Margin
=
50
};//表明图象周围的空间 QToolButton
*
zoomInButton; QToolButton
*
zoomOutButton; QMap
<
int
, QVector
<
QPointF
>
>
curveMap;
//
曲线数据
QVector
<
PlotSettings
>
zoomStack;
//
PlotSettings栈区域
int
curZoom;
bool
rubberBandIsShown; QRect rubberBandRect; QPixmap pixmap;
//
显示在屏幕的控件的一个拷贝,任何绘制总是先绘制在离线pixmap上,然
后拷贝到控件上
};
//
PlotSettings确定x,y轴的范围,和刻度的个数
class
PlotSettings {
public
: PlotSettings();
void
scroll(
int
dx,
int
dy);
void
adjust();
double
spanX()
const
{
return
maxX
-
minX; }
double
spanY()
const
{
return
maxY
-
minY; }
double
minX;
double
maxX;
int
numXTicks;
double
minY;
double
maxY;
int
numYTicks;
private
:
static
void
adjustAxis(
double
&
min,
double
&
max,
int
&
numTicks); };
#endif
图5-8表示了Plotter控件和PlotSettings的关系。
通常,numXTicks和numYTicks是有一个的误差,如果numXTicks为5,实际上Plotter会在x轴上绘制6个刻度。这样可以简化以后的计算(至于怎么样简化的,就看程序和后文吧)。
Figure 5-8 PlotSettings's member variables
现在来看源文件(代码有些长,先用代码格式给出完整源文件代码):
#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) {
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); } }
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) {
const
int
MinTicks
=
4
;
double
grossStep
=
(max
-
min)
/
MinTicks;
double
step
=
pow(
10.0
, floor(log10(grossStep)));
if
(
5
*
step
<
grossStep) { step
*=
5
; }
else
if
(
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; }
顶
0
踩
0
上一篇(转) 5.3把自定义控件集成到Qt Designer中(Integrating Custom Widgets with Qt Designer) 下一篇(转)5.4 双缓冲技术(Double Buffering)-2
我的同类文章
Qt(31)
http://blog.csdn.net
•(转)利用Qt的qmake创建vc工程2009-08-28阅读1936 •(转)QT中画图方法的不同 2009-06-18阅读3727 •(转)5.4 双缓冲技术(Double Buffering)-42008-05-20阅读2422 •(转)5.4 双缓冲技术(Double Buffering)-22008-05-20阅读1212 •(转)5.2从QWidget派生(Subclassing QWidget)2008-05-12阅读1249
•(转)QT事件机制2009-06-20阅读1630 •(转)5.4 双缓冲技术(Double Buffering)-52008-05-20阅读931 •(转)5.4 双缓冲技术(Double Buffering)-32008-05-20阅读1901 •(转) 5.3把自定义控件集成到Qt Designer中(Integrating Custom Widgets with Qt Designer)2008-05-12阅读4155 •(转)5.1自定义Qt控件(Customizing Qt Widgets)2008-05-12阅读1795
更多文章