qt项目-《图像标注软件》源码阅读笔记-Label 2d绘制图片及标注类

目录

1. Command 概览   

1.1 功能

1.2 字段

1.3 方法

2. 源码细节

2.1 paintEvent

2.2 mousePressEvent

2.3 mouseMoveEvent

2.4 mouseReleaseEvent


 

1. Command 概览   

1.1 功能

2d绘制图片及标注类,继承QLabel

内部具体的形状的绘制均交由Shape类进行处理,
Shape类为形状基类,
具体的绘制则会调用对应形状类的虚函数。

1.2 字段

  1. color:默认标注形状颜色
  2. current:默认-1,current表示当前操作的标注形状的索引
  3. pixmap:中心图片
  4. size:默认标注形状大小
  5. zoomLevel:默认放大倍数
  6. MagniFier:是否开启放大镜
  7. manager:存放2d中心组件
  8. XOffsetSum:每次移动标注形状的总偏移量
  9. YOffsetSum:每次移动标注形状的总偏移量
  10. shapes:标注形状列表
  11. status:标注状态,默认为noshape

1.3 方法

  1. paintEvent:重写绘制事件的处理
  2. mousePressEvent:重写鼠标按压事件的处理
  3. mouseMoveEvent:重写鼠标移动事件的处理
  4. mouseReleaseEvent:重写释放鼠标事件的处理

2. 源码细节

构造函数,初始化字段:

color默认标注形状颜色, pixmap中心图片, magnifierArea右下角放大区域图片;

    /// \brief 构造函数
    Label(QWidget* parent):QLabel(parent){
        color.setRgb(100,255,0,100);
        setMouseTracking(true);
        pixmap=new QPixmap();
        setAlignment(Qt::AlignCenter);
        magnifierArea.load(":/icons/icons/temp.jpg"); // private: 右下角放大区域图片
    }

2.1 paintEvent

在处理放大图像问题。如何缩放图像。

/// \brief 重写绘制事件的处理
void My::Label::paintEvent(QPaintEvent *event){

    //空图片则返回
    if(pixmap->isNull()){return;}

    //根据放大倍数进行放大
    QPixmap tempPixMap(*pixmap);  // pixmap中心图片. 缩放图像
    tempPixMap=tempPixMap.scaled(tempPixMap.width()*zoomLevel,tempPixMap.height()*zoomLevel,Qt::KeepAspectRatio);

    //label进行响应的大小调整        // 根据缩放后的图片大小调整当前 Label 的大小,确保 Label 能容纳整个图片
    this->resize(tempPixMap.width(),tempPixMap.height());

    //绘制图片
    QPainter painter(this);       // 把缩放后的图像绘制到新label中
    painter.drawPixmap(0,0,tempPixMap.width(),tempPixMap.height(),tempPixMap);

    //绘制形状
    foreach(My::Shape2D* shape,shapes){  // 把所有shape绘制到label中。
        shape->draw(this);
    }

    //根据是否开启放大镜,进行绘制
    if(MagniFier){
        QPainter painter(this);
        painter.drawPixmap(this->width()-100,this->height()-100,100,100,magnifierArea);
    }
}

2.2 mousePressEvent

标注时,选择不同的shape,生成不同的shape对象并更新其成员。 

/// \brief 重写鼠标移动事件的处理
void My::Label::mousePressEvent(QMouseEvent *event){

    //更新当前鼠标所在位置
    float x=event->x();  // 在label中的位置,也就是真实像素坐标
    float y=event->y();
    cursorX=x;
    cursorY=y;

    //更新鼠标点位,该点位存储横纵坐标比例.   相对于label的比例位置。
    QPointF p(qreal(x/this->width()),qreal(y/this->height()));  // qreal表示浮点数

    //右键则直接返回,右键会弹出菜单,所以此处不进行处理
    if(event->buttons()&Qt::RightButton)return;

    //若为noshape状态
    if(status==My::NoShape){  // 鼠标还没选择标注shape
        if(current==-1)return;  // 没选择shape,也没选中已标注.current表示当前操作的标注形状的索引
        else{  // 没选择shape,但选中了已标注信息。

            //当前current不为-1,则发送信号
            emit(manager->selectedChanged(current,false));  // 告诉centralW已选中那个标注。
            current=-1;
        }
        return;
    }

    //若为inshape状态,即鼠标在标注形状内部
    if(status==My::InShape){

        //判断是否仍在标注形状内部,若在则更新current
        int index=shapes.length();  // 鼠标点位p所在的shape index
        for(int i=0;i<shapes.length();i++){
            if(shapes[i]->isInShape(p,this)){  // 鼠标点位p是在哪个shape内部
                index=i;
                break;
            }
        }
        if(index<shapes.length()){  // 说明鼠标确实在shapes[index]内部
            current=index;  // 更新当前操作的标注形状索引
        }

        //设置鼠标样式
        setCursor(Qt::ClosedHandCursor);  // 封闭手的形状,通常表示用户可以按住鼠标左键并拖动
        emit(manager->selectedChanged(current,true));  // 告诉centralW已选中那个标注。
    }

    
    //若为创建矩形状态
    if(status==My::RectangleShape){

        if(current==-1){  // 没有操作现有的标注
            My::Rectangle* rectangle=new My::Rectangle();  //新建一个矩形
            rectangle->points.append(p);  // 并设置其成员:points,color
            rectangle->color=color;
            shapes.append(rectangle);
            current=shapes.length()-1;
        }

        //若已添加形状,则根据鼠标所在位置更新矩形位置,且询问是否添加
        else{
            My::Rectangle* rectangle=dynamic_cast<My::Rectangle*>(shapes[current]);
            rectangle->width=(x-this->width()*rectangle->points[0].x())/this->width();
            rectangle->height=(y-this->height()*rectangle->points[0].y())/this->height();
            update();

            //询问是否添加
            bool isOk;
            QString text=QInputDialog::getText(this,"label me!","Please input the label",QLineEdit::Normal,"",&isOk);
            if(isOk){
                rectangle->label=text;

                //命令栈记录
                manager->command->logAdd(current);

                //发送信号
                emit(manager->labelAdded(rectangle,current));
                current=-1;
            }
        }
        return;
    }

    ...其他shape

}

2.3 mouseMoveEvent

放大镜功能。是否在shape内部。

/// \brief 重写鼠标移动事件的处理
void My::Label::mouseMoveEvent(QMouseEvent *event){

    //状态栏显示坐标
    MainWindow* w=qobject_cast<MainWindow*>(manager->parent());

    //获取更新鼠标点位
    float x=event->x();  // 真实像素坐标
    float y=event->y();
    int index=shapes.length();
    QPointF p(qreal(x/this->width()),qreal(y/this->height()));  // 相对label点位
    
    // 鼠标移动时,在状态栏显示真实像素坐标
    /*MainWindow* */w=qobject_cast<MainWindow*>(manager->parent());
    w->statusBar()->showMessage(QString("Pos: X %1 Y %2").arg(x).arg(y),1000);

    //是否有放大镜,若有则会实时获取鼠标当前的图片区域
    if(MagniFier){
        QPixmap tempPixMap;
        tempPixMap=tempPixMap.grabWidget(this,int(x),int(y),20,20);  // 获取以 (x, y) 为左上角的 20x20 区域的截图。
        magnifierArea=tempPixMap.scaled(100,100,Qt::KeepAspectRatio);  // 截图缩放为 100x100 的大小, 保持宽高比
        qDebug()<<"success"<<endl;
        update();
    }

    //noshape状态
    if(status==My::NoShape){

        //更新鼠标位置
        cursorX=x;
        cursorY=y;

        //判断是否鼠标在标注形状内,并更新
        for(int i=0;i<shapes.length();i++){
            if(shapes[i]->isInShape(p,this)){
                index=i;
                break;
            }
        }
        if(index<shapes.length()){  // 在标注形状内部,则
            setCursor(Qt::OpenHandCursor);  // 将光标设置为开放手光标,表示可以拖动。
            status=My::InShape;
            current=index;
            shapes[current]->isHover=true;
            update();
            return;
        }
        setCursor(Qt::ArrowCursor);
        return;
    }

    //inshape状态
    if(status==My::InShape){

        //若为左键,则为移动标注形状的位置
        if(event->buttons()&Qt::LeftButton){
            float xOffset=(x-cursorX)/this->width();
            float yOffset=(y-cursorY)/this->height();

            //更新总偏移量
            XOffsetSum+=xOffset;
            YOffsetSum+=yOffset;

            cursorX=x;
            cursorY=y;

            //标注形状进行偏移
            shapes[current]->offset(xOffset,yOffset);
            update();
            return;
        }

        //判断是否鼠标是否在标注内并更新
        for(int i=0;i<shapes.length();i++){
            if(shapes[i]->isInShape(p,this)){
                index=i;
                break;
            }
        }

        if(index<shapes.length()){
            current=index;
            shapes[current]->isHover=true;
            update();
        }

        if(index>=shapes.length()){
            status=My::NoShape;
            setCursor(Qt::ArrowCursor);
            if(current!=-1)shapes[current]->isHover=false;
            update();
            return;
        }
        cursorX=x;
        cursorY=y;
        return;

    }


    //创建矩形状态
    if(status==My::RectangleShape){

        cursorX=x;
        cursorY=y;

        //同步更新矩形的位置
        if(current!=-1){
            My::Rectangle* rectangle=dynamic_cast<My::Rectangle*>(shapes[current]);
            rectangle->color=color;
            rectangle->width=(x-rectangle->points[0].x()*this->width())/this->width();
            rectangle->height=(y-rectangle->points[0].y()*this->height())/this->height();
            update();
        }
        return;
    }
}

2.4 mouseReleaseEvent

shape内部拖动。

/// \brief 重写释放鼠标事件的处理
void My::Label::mouseReleaseEvent(QMouseEvent *event){
    if(status==My::NoShape){
        return;
    }
    if(status==My::InShape){  // 在shape内部
        setCursor(Qt::OpenHandCursor);  // 为开放手光标,表示可以拖动。

        //命令栈记录总偏移量
        if(event->button()==Qt::LeftButton){  // 如果是左键,则是拖动功能。
            manager->command->logMove(current,XOffsetSum,YOffsetSum);
            XOffsetSum=0;
            YOffsetSum=0;
        }
        return;
    }
    if(status==My::BrushShape){  // 目前在shape内部只有拖动功能,其他各自的功能可以在此添加。
        return;
    }
    if(status==My::RectangleShape){
        return;
    }
    if(status==My::PolygonsShape){
        return;
    }
    if(status==My::CircleShape){
        return;
    }
    if(status==My::CurveShape){
        return;
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mr.Q

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值