1.关于paintView重绘问题
正常情况只需要如下代码,解释,由于paintEvent采用了viewport()这里也使用viewport,另外由于重写的类的基类不是paintEvent,所以也尽量用viewport(),这边没有具体用代码实践过,有时间再补充
//Todo
this->viewport()->update();
此外有另外一种情况:这情况是这样的,在写绘画功能的时候,加入了双缓存绘图方法,撤回时保存的是上次lastPix,但其他条件一样,当正是画图时,不执行tempix,但是执行nowPix的重绘加之前的action,就变成了,当调用update()函数时,其实是调用了重绘函数,但是同时重绘了nowPix的action,展现的情况就是,相当于paintView没有重绘成功,因为显示是一样的。实际上,是调用updete()函数重绘成功了,但同时执行了action,所以,避免这种情况出现,需要跳过action,直接执行drawPixmap的函数。
void DrawScene::paintEvent(QPaintEvent *event)
{
//! 双缓冲绘图,原理是在拖动过程中先把原来的图形复制到tempPix里面并在tempPix里面画,我们此时看到的就是在tempPix里的图形。只在鼠标释放的时候才在pix绘一次。
int x, y, w, h;
x = lasetPoint.x();
y = lasetPoint.y();
w = endPoint.x() - x;
h = endPoint.y() - y;
//! pixRect为画布大小,当mousePress的lasetPoint或mouseRelease的endPoint不在pixRect上,不执行paintView。
if(!pixRect.contains(lasetPoint)||!pixRect.contains(endPoint)){
return;
}
QPainter painter(this->viewport());
if (isDrawing)
{
...
painter.drawPixmap(0, 0, tempPix);
}
else
{
//! 双缓存,实际画图部分
QPainter pp(&pix);
//! 当不是withdraw按钮,或者actionNull选择时候(除了两者其他动作,都执行withdrawGroup)
//! 跳过action
if(actionMode!=Withdraw && actionMode!=ActionNull){
withdrawPix=pix;
withdrawGroup.append(pix);
}
switch (actionMode) {
...
case Withdraw:
//! 执行完withdraw,马上设置为原来的actionMode
actionMode=lastAtionMode;
break;
case Redraw:
actionMode=lastAtionMode;
break;
}
painter.drawPixmap(0, 0, pix);
QVector<QPointF> nullGroup;
pointGroup.swap(nullGroup);
}
QGraphicsView::paintEvent(event);
}
2.关于鼠标(point)是否落在画布上的判断
pixRect为画布大小,当mousePress的lasetPoint或mouseRelease的endPoint不在pixRect上,不执行paintView。
if(!pixRect.contains(lasetPoint)||!pixRect.contains(endPoint)){
return;
}
3.关于撤回withdraw()和redraw()功能能实现
3.1withdraw():
这里代码涉及的东西有点多,主要讲述原理,和分几个步骤。
原理:用数组QVector<QPixmap>withdrawGroup记录每次action的pix,当需要退回的时候,执行withdraw(),就会退回上一次的lastPix,同时把当次的nowPix,加入到redrawGroup,当每次不是退回,而是执行其他命令时,清除redrawGroup。这基本符合要求。
一,初始化
先用保存每次action的后的pix.这一步很重要,原因是,withdraw()和redraw()要配合,redrawGroup是保存withdrawGroups删除的pix,如果withdrawGroup,保存的是action前的pix,就无法用redraw()恢复到最新的,所以withdrawGroups只能保存action后的pix,这样问题就来了,由于是保存action后pix,当退回到处原始的时候,会发现是第一次action后的pix,而不是初始的pix,所以才有了这一步。
QVector<QPixmap> withdrawGroup;
withdrawGroup.append(pix);
二、withdraw()和redraw()函数
void DrawScene::withdraw()
{
//这里判断主要是由于上一,添加的初始pix,所以退回到这里后就停止
if(withdrawGroup.length()>1){
//这里是改变执行的action的mode,如果不声明,会不能退回,原因是actionMode不变
//action会重新执行的,
//效果变成:相当于没有执行退回,
//实际执行了退回回,但又重绘了一次
actionMode=Withdraw;
//先把pix加入保存复原的redrawGroup
redrawGroup.append(pix);
//再把在withdrawGroup中pix删去
withdrawGroup.pop_back();
//当前的pix等于删去最新的action的pix
pix=withdrawGroup.last();
//执行重绘
this->viewport()->update();
//当退回到初始pix,通知父界面已经不可以退回并禁用按钮
if(withdrawGroup.length()==1){
emit sendDraw(WithdrawOff);
}
//每次退回,都通知父界面恢复键可用
emit sendDraw(RedrawOn);
}
}
//原理基本和上面一致
void DrawScene::redraw()
{
if(redrawGroup.length()>0){
actionMode=Redraw;
pix=redrawGroup.last();
withdrawGroup.append(pix);
redrawGroup.pop_back();
qDebug()<<"recoverGroup.pop_back.length"<<redrawGroup.length();
this->viewport()->update();
if(redrawGroup.length()==0){
emit sendDraw(RedrawOff);
}
}
}
三、paintView()
注意点:
- 当actionMode为初始的actionNull时,withdrawGroup不执行对pix的保存,原因是很多事件,自动执行paintView()的重绘,如果都进行,重绘,会发现无法退回,或者需要退回非常多次,容易变成,不知道自己有没有点退回。
- 点击事件,即使在点击界面外部仍然会执行paintView()的重绘。
//当actionMode为初始的actionNull时,不执行对pix的保存,同理,由于点击事件,几时点击界面外部
if(actionMode!=ActionNull &&lastEndPoint!=endPoint){
lastEndPoint=endPoint;
withdrawPix=pix;
withdrawGroup.append(pix);
//qDebug()<<"backupGroup.length"<<withdrawGroup.length();
emit sendDraw(WithdrawOn);
if(redrawGroup.length()>0) {
QVector<QPixmap> redrawNullGroup;
redrawGroup.swap(redrawNullGroup);
emit sendDraw(RedrawOff);
}
}