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

发布时间:2023年12月25日

目录

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;
    }
}

文章来源:https://blog.csdn.net/jizhidexiaoming/article/details/135192992
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。