效果:
1、当鼠标再矩形内/上的时候,线的颜色就加深,不在矩形内就不加深;
2、当鼠标在矩形四个叫的时候,出现红色的圈圈提醒选中了角;
3、选中四个角中的一个角,按住移动鼠标实现拉伸;
4、鼠标在矩形内,按住鼠标实现移动。
思路1__变量讲解:
因为我的代码是直接在公司的项目中写入的,这里就主要贴我写的代码部分还有思路。
1、定义的变量:
struct LINEINFO
{
//定义了7个点,因为我当时要画的有线、矩形、椭圆;
//x0 y0用来记录鼠标按下的点,这个是绘制的图像移动时候需要使用到的;
//x1 y1 x2 y2鼠标按下与松开的位置,可以理解为起点和重终点;
//x3,y3,x4,y4,x5,y5,x6,y6 这是四个点的坐标,当然你不定义也可以,我是为了方便画点的时候方便才定义的;
//mTempX1,mTempX2,mTempY1,mTempY2 用于保存图像移动前x1 y1 x2 y2的位置,图像移动的时候需要用到
int x0,y0,x1,x2,y1,y2,x3,y3,x4,y4,x5,y5,x6,y6;
int mTempX1,mTempX2,mTempY1,mTempY2;
int mSelPointNum; //选中了第几个点
bool mbSelLine; //选择直线
bool mbSelPoint; //选中端点
bool mbDraw; //是否绘制
LINEINFO()
{
mbSelLine = false; //是否选中直线
mbSelPoint = false; //是否选中点
mbDraw = false; //选中哪个点
mSelPointNum = 0;
}
};
LINEINFO mLineInfo;
QLine mLine;
QRect mRect;
int mDrawSel; //绘制哪一种类型,我是 0 1 2分别对应线、矩形、椭圆
int mPressClick; //鼠标点击的次数,用来判断是否清空绘制好的线
bool mbRelease; //鼠标是否松开
思路2__原理讲解
1、其实画各种形状的图案很简单,不外乎就是起点和终点两个点,这个要明白,就是按下的那个点和松开鼠标时的那两个点
2、鼠标松下时重置起点和终点的位置,确保我们的起点是在终点的左上角。为什么要这样呢?因为我们在拉伸图案的时候我们的起点和终点是会出现变化的,重置可以避免bug的出现。
3、选中点:我是以鼠标的坐标在端点的5个单位的圆内为选中的,让用户更容易选中点
4、选中线/在图案内:这个网上有计算公式,有一点我想说的是,线的方程存在一点点的误差,因为我们的计算机是用二进制存的,所以判断点是否在线上的时候要优化一下,这里先不说,后面有代码讲解。
5、图案移动:我们按下的点x0 y0做为标记点, 松开的点候的点与 (x0 ,y0)在x轴与y轴的偏移差就是图案的位置,因为这个时候我们的x1 y1 x2 y2这两个点的坐标已经变化了,所以mTempX1,mTempX2,mTempY1,mTempY2这两个点用来记住x1 y1 x2 y2的坐标就起到了至关重要的作用。
6、图案选中顶点拉伸:更新 (x1,y1) 或(x2,y2)的坐标
代码部分:
1、构造函数
mDrawSel = 1; //选择画什么形状
mPressClick = 0; //点击了多少下
mbRelease = false; //鼠标松开
注意:以下的事件类型可能与你们的不同,但是没有关系,每个类中都有这样的事件,你找到你哪个类相对于的事件类就行,可以不依照我的类
2、鼠标hover事件
if(mLineInfo.mbDraw)
{
//判断是否在点上
isPoint(event->pos().x(), event->pos().y());
//判断是否在线上
isLine(event->pos().x(), event->pos().y());
//如果在点上那就默认不在线上
if(mLineInfo.mbSelPoint)
{
mLineInfo.mbSelLine = false;
}
//更新绘图事件
mLineInfo.mbDraw = true;
update();
}
3、判断是否在点上
//这个数字的排布是矩形四个角的排布情况,1和2分别为起点和重点
// 1 4
// 3 2
mLineInfo.x3 = mLineInfo.x1;
mLineInfo.y3 = mLineInfo.y2;
mLineInfo.x4 = mLineInfo.x2;
mLineInfo.y4 = mLineInfo.y1;
// qDebug() << "x3:" << mLineInfo.x3 << "y3:" << mLineInfo.y3 << "x4:" << mLineInfo.x4 << "y4:" << mLineInfo.y4 ;
//这里就是点到圆的距离的平方
int R1 = (x-mLineInfo.x1)*(x-mLineInfo.x1) + (y-mLineInfo.y1)*(y-mLineInfo.y1);
int R2 = (x-mLineInfo.x2)*(x-mLineInfo.x2) + (y-mLineInfo.y2)*(y-mLineInfo.y2);
int R3 = (x-mLineInfo.x3)*(x-mLineInfo.x3) + (y-mLineInfo.y3)*(y-mLineInfo.y3);
int R4 = (x-mLineInfo.x4)*(x-mLineInfo.x4) + (y-mLineInfo.y4)*(y-mLineInfo.y4);
// mRadius = 10;这个是我在全局中定义的,位的是后续的更改
//判断点的距离以及选中的点
if(R1 <= mRadius*mRadius/4)//半径5之内的圆
{
qDebug() << "点1___";
mLineInfo.mSelPointNum = 1; //选中了第一个点
mLineInfo.mbSelPoint = true;
}
else if(R2 <= mRadius*mRadius/4)
{
qDebug() << "点2___";
mLineInfo.mSelPointNum = 2;
mLineInfo.mbSelPoint = true;
}
else if(R3 <= mRadius*mRadius/4)
{
qDebug() << "点3___";
mLineInfo.mSelPointNum = 3;
mLineInfo.mbSelPoint = true;
}
else if(R4 <= mRadius*mRadius/4)
{
qDebug() << "点4___";
mLineInfo.mSelPointNum = 4;
mLineInfo.mbSelPoint = true;
}
else
{
mLineInfo.mSelPointNum = 0;
mLineInfo.mbSelPoint = false;
}
4、点在矩形上/内,这个算法是网上百度到的,直接百度点在矩形内的公式就有了
if(x >= mLineInfo.x1 && x <= mLineInfo.x2 && y >= mLineInfo.y1 && y <= mLineInfo.y2) //再矩形上/矩形内
{
mLineInfo.mbSelLine = true;
}
else
{
mLineInfo.mbSelLine = false;
}
5、鼠标按下
void GraphicsPixmapItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
//按下的时候,那松开就别为false,用于hover状态是画笔的颜色改变
mbRelease = false;
if(mLineInfo.mbSelPoint)
{
qDebug() << "Press__选中了点";
mPressClick = 1;
}
else if(mLineInfo.mbSelLine)
{
qDebug() << "Press____选中了线";
//记录按下是的坐标
mLineInfo.x0 = event->pos().x();
mLineInfo.y0 = event->pos().y();
mPressClick = 1;
}
else
{
if(event->button() == Qt::LeftButton) // 左键
{
if(mPressClick == 0)
{
mLineInfo.x1 = event->pos().x();
mLineInfo.y1 = event->pos().y();
mPressClick++;
}
//清除绘制的图案
if(mLineInfo.mbDraw && !mLineInfo.mbSelLine && !mLineInfo.mbSelPoint) //没有选中点/线
{
mPressClick = 0;
mLineInfo.mbDraw = false;
update();
}
}
//清楚绘制的图案
else if(event->button() == Qt::RightButton)//右键
{
mPressClick = 0;
mLineInfo.mbDraw = false;
update();
}
}
}
6、鼠标移动事件
void GraphicsPixmapItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
int x = event->pos().x();
int y = event->pos().y();
if(mPressClick == 1)
{
if(mLineInfo.mbSelLine) //直线被选中
{
// 点的偏移量
int _x = event->pos().x() - mLineInfo.x0;
int _y = event->pos().y() - mLineInfo.y0;
//mTempX1 mTempX2 mTempY1 mTempY2的复制在鼠标松开的事件中会更新,代码在后面
//这里就涉及到一点点的思路问题了,就是一个偏移量的问题,你拿笔画两条线
//第一条线的端点和末尾分别为起点与终点,中间为x01 y0
//再画另一条线,计算在起点和终点的位置应该在哪个位置即可。
//当前的x,y为x0 y0这个点的另一个坐标
mLineInfo.x1 = mLineInfo.mTempX1 + _x;
mLineInfo.x2 = mLineInfo.mTempX2 + _x;
mLineInfo.y1 = mLineInfo.mTempY1 + _y;
mLineInfo.y2 = mLineInfo.mTempY2 + _y;
}
//这下面这些代码我就贴矩形选中点的代码
else if(mLineInfo.mbSelPoint && mDrawSel == 0)//直线选中点
{
isSelLinePoint(x, y);
}
else if(mLineInfo.mbSelPoint && mDrawSel == 1) //矩形选中点
{
isSelRectPoint(x, y);
}
else if(mLineInfo.mbSelPoint && mDrawSel ==2) //椭圆选中了点
{
isSelEllipsePoint(x, y);
}
else
{
//正常画线
mLineInfo.x2 = x;
mLineInfo.y2 = y;
}
mLineInfo.mbDraw = true;
update();
}
}
7、矩形选中点:
void GraphicsPixmapItem::isSelRectPoint(int x, int y)
{
//依据你选中的点去更改(x1,y1)或者(x2,y2)的坐标
//反正你拉伸图案,总有一个点是固定的,一个点是变化的
if(mLineInfo.mSelPointNum == 1)
{
qDebug() << "选中点1_矩形";
mLineInfo.x1 = mLineInfo.mTempX2;
mLineInfo.y1 = mLineInfo.mTempY2;
mLineInfo.x2 = x;
mLineInfo.y2 = y;
}
if(mLineInfo.mSelPointNum == 2) //不变
{
qDebug() << "选中了2_矩形";
mLineInfo.x2 = x;
mLineInfo.y2 = y;
}
if(mLineInfo.mSelPointNum == 3)
{
qDebug() << "选中了3_矩形";
mLineInfo.x1 = mLineInfo.x4;
mLineInfo.y1 = mLineInfo.y4;
mLineInfo.x2 = x;
mLineInfo.y2 = y;
}
if(mLineInfo.mSelPointNum == 4)
{
qDebug() << "选中了4_矩形";
mLineInfo.x1 = mLineInfo.x3;
mLineInfo.y1 = mLineInfo.y3;
mLineInfo.x2 = x;
mLineInfo.y2 = y;
}
}
8、鼠标松开事件
void GraphicsPixmapItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
//
mPressClick = 0;
// 矩形/椭圆存在上下反转的情况
//重置(x1,y1),(x2,y2)的位置,确保起点在终点的右上角。
//因为用户在翻转的时候可能会打乱我们定义的那几个点的位置,重置点更保险
if(mDrawSel == 1 )//绘制的是矩形
{
//矩形
int w = abs(mLineInfo.x1 - mLineInfo.x2);
int h = abs(mLineInfo.y1 - mLineInfo.y2);
if((mLineInfo.x1 <= mLineInfo.x2) && mLineInfo.y1 >= mLineInfo.y2)
{
qDebug() << "2_右上角";
mLineInfo.y1 = mLineInfo.y1 - h ;
mLineInfo.y2 = mLineInfo.y2 + h ;
}
if((mLineInfo.x1 >= mLineInfo.x2) && (mLineInfo.y1 >= mLineInfo.y2))
{
qDebug() << "2_左上角";
int tempX = mLineInfo.x1;
int tempY = mLineInfo.y1;
mLineInfo.x1 = mLineInfo.x2;
mLineInfo.y1 = mLineInfo.y2;
mLineInfo.x2 = tempX;
mLineInfo.y2 = tempY;
}
if((mLineInfo.x1 >= mLineInfo.x2) && (mLineInfo.y1 <= mLineInfo.y2))
{
qDebug() << "2_左下角";
mLineInfo.x1 = mLineInfo.x1 - w ;
mLineInfo.x2 = mLineInfo.x2 + w ;
}
}
//将起点与终点的值保存起来,用于平移图案
mLineInfo.mTempX1 = mLineInfo.x1;
mLineInfo.mTempY1 = mLineInfo.y1;
mLineInfo.mTempX2 = mLineInfo.x2;
mLineInfo.mTempY2 = mLineInfo.y2;
mbRelease = true;
9、绘图事件
painter->setRenderHint(QPainter::Antialiasing, true); //抗锯齿
painter->setPen(QPen(Qt::yellow,2));//设置画笔形式
if(mLineInfo.mbSelLine && mbRelease)
{
painter->setPen(QPen(Qt::yellow,4));//设置画笔形式_加粗
}
if(mLineInfo.mbDraw) //画线
{
int w,h;
if(mDrawSel == 0)//直线
{
mLine.setLine(mLineInfo.x1,mLineInfo.y1,mLineInfo.x2,mLineInfo.y2);
painter->drawLine(mLine);
}
else if(mDrawSel == 1)//矩形
{
w = mLineInfo.x2 - mLineInfo.x1;
h = mLineInfo.y2 - mLineInfo.y1;
mRect.setRect(mLineInfo.x1,mLineInfo.y1,w,h);
painter->drawRect(mRect);
}
else if(mDrawSel == 2)//椭圆
{
w = mLineInfo.x2 - mLineInfo.x1;
h = (mLineInfo.y2 - mLineInfo.y1);
mRect.setRect(mLineInfo.x1,mLineInfo.y1,w,h);
painter->drawEllipse(mRect);
}
}
if(mLineInfo.mbSelPoint && mbRelease && mDrawSel!=2) // 线/矩形画点
{
painter->setPen(QPen(Qt::red));//设置画笔形式
if(mLineInfo.mSelPointNum == 1)
{
mRect.setRect(mLineInfo.x1-mRadius/2, mLineInfo.y1-mRadius/2, mRadius, mRadius);
}
if(mLineInfo.mSelPointNum == 2)
{
mRect.setRect(mLineInfo.x2-mRadius/2, mLineInfo.y2-mRadius/2, mRadius, mRadius);
}
if(mLineInfo.mSelPointNum == 3)
{
mRect.setRect(mLineInfo.x3-mRadius/2, mLineInfo.y3-mRadius/2, mRadius, mRadius);
}
if(mLineInfo.mSelPointNum == 4)
{
mRect.setRect(mLineInfo.x4-mRadius/2, mLineInfo.y4-mRadius/2, mRadius, mRadius);
}
painter->drawEllipse(mRect);
}
到这里就结束了,有任何问题可以私聊我。