Painter画图形_可拖拽与拉伸

效果:

在这里插入图片描述
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);
    }

到这里就结束了,有任何问题可以私聊我。

  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值