QT PC端实现图片裁剪选择框

6 篇文章 0 订阅

需求

实际项目中经常需要选择本地图片的部分预览、剪切、上传处理后图片到服务端,上图,效果图:
在这里插入图片描述
这个是最近QT实现的PC项目里的需求和成品截图。这里讲讲上图红色圈圈里的选择框实现。

需求分析

UI实现:外侧需要半透明蒙层,虚线的选择框和四个缩放用的顶点,内侧透明镂空交互:四个点可以缩放,镂空处可以移动,且边框边界限制

开发实现

实现比较简单,直接上代码,具体解释在代码里都有了
ui文件:

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>SelectRectWidget</class>
 <widget class="QWidget" name="SelectRectWidget">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>400</width>
    <height>300</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Form</string>
  </property>
 </widget>
 <resources/>
 <connections/>
</ui>


头文件:

#ifndef SELECTRECTWIDGET_H
#define SELECTRECTWIDGET_H


#include <QWidget>
#include <QPen>
#include <QLabel>
#include <functional>


namespace Ui {
class SelectRectWidget;
}
class SelectRectWidget : public QWidget
{
    Q_OBJECT


public:
    explicit SelectRectWidget(QWidget *parent = nullptr);
    ~SelectRectWidget();


    /**
     * @brief setFixCenterRectRatio 设置选择框为固定比例
     * @param ratio 比例值width:height,比如16:9
     */
    void setFixCenterRectRatio(float ratio);


    //中间镂空区域的Rect
    QRect centerRect() const;


    /**
     * @brief setSelectRectChange 设置选择框变化时回调函数
     * @param selectRectChange
     */
    void setSelectRectChange(const std::function<void (QRect rect)> &selectRectChange);


protected:
    virtual void paintEvent(QPaintEvent *event) override;
    bool eventFilter(QObject *, QEvent *) override;


private:
    //根据centerRect_设置各种Rect
    void setRects();
    //检测centerRect区域值是否有效
    void checkCenterRect();


private:
    Ui::SelectRectWidget *ui;
    QRect leftRect_;//中间镂空区域的左侧蒙层Rect
    QRect topRect_;//中间镂空区域的顶部蒙层Rect
    QRect rightRect_;//中间镂空区域的右侧蒙层Rect
    QRect bottomRect_;//中间镂空区域的底部蒙层Rect
    QRect centerRect_;//中间镂空区域的Rect
    QRect preCenterRect_;//记录上一次中间镂空区域的Rect,避免中间区域相同时重复绘制
    QColor outsideColor_;//外部蒙层的颜色值
    QColor insideColor_;//内部镂空区域的颜色值
    QColor dashColor_;//中间镂空区域的虚线边框的颜色值


    QPen dashPen_;//中间镂空区域的虚线边框的Pen(可以设置虚线长度和间隔长度等)
    QVector<qreal> dashes_;//dashPen_的虚线长度和间隔长度设置


    QLabel * labelLeftBottom_ = nullptr;//可以触发缩放,镂空区域虚线边框左下部的顶点
    QLabel * labelRightBottom_ = nullptr;//可以触发缩放,镂空区域虚线边框右下部的顶点
    QLabel * labelLeftTop_ = nullptr;//可以触发缩放,镂空区域虚线边框左上部的顶点
    QLabel * labelRightTop_ = nullptr;//可以触发缩放,镂空区域虚线边框右上部的顶点
    QRect    dragMoveLocation_;//保存centerRect_移动时的初始值
    QPoint   dragZoomPos_;//四个顶点被QMouseEvent press时的初始值
    bool     dragZoomRunning_;//四个顶点被QMouseEvent press时的
    QRect    dragZoomLocation_;//四个顶点被QMouseEvent press时,保存centerRect_的初始值
    bool     press_ = false;//鼠标press centerRect_区域时为true
    QPoint   startPos_;//保存鼠标press时的QMouseEvent初始值
    float    fixCenterRectRatio_;//选择框固定比例值缩放,不设置该值时非固定比例缩放
    std::function<void (QRect rect)> selectRectChange_;//选择框变化时回调函数


    static int  sDragDotWidth_;      //选择框边线上几个可以放大缩小的点大小
    static int  sCenterRectMinWidth_;//选择框的最小宽度和高度,防止选择框边线重叠
    static int  sLineWidth_;//选择框边线宽度
    static int  sDragDotOffset_;//四个顶点的offset值


protected:
    virtual void mousePressEvent(QMouseEvent *e) override;
    virtual void mouseMoveEvent(QMouseEvent *e) override;
    virtual void mouseReleaseEvent(QMouseEvent *e) override;
};


#endif // SELECTRECTWIDGET_H


cpp文件:

#include "selectrectwidget.h"
#include "ui_selectrectwidget.h"


#include <QPainter>
#include <QVector>
#include <QMouseEvent>


#include "qdebug.h"


int  SelectRectWidget::sDragDotWidth_ = 6;
int  SelectRectWidget::sDragDotOffset_ = 2;
int  SelectRectWidget::sCenterRectMinWidth_ = 6;
int  SelectRectWidget::sLineWidth_ = 1;


void SelectRectWidget::setRects()
{
    leftRect_.setRect(0,0,centerRect_.x(),height());
    topRect_.setRect(centerRect_.x(),0,centerRect_.width()+sLineWidth_,centerRect_.y());
    rightRect_.setRect(centerRect_.width()+centerRect_.x()+sLineWidth_,0,width()-centerRect_.width()-centerRect_.x(),height());
    bottomRect_.setRect(centerRect_.x(),centerRect_.height()+centerRect_.y()+sLineWidth_,centerRect_.width()+sLineWidth_,height()-centerRect_.height()-centerRect_.y());
}


SelectRectWidget::SelectRectWidget(QWidget *parent) :
    QWidget(parent),fixCenterRectRatio_(-1.0f),
    ui(new Ui::SelectRectWidget)
{
    ui->setupUi(this);
    //绘制区域
    setRects();
    centerRect_.setWidth(0);


    //区域内外等颜色值
    outsideColor_ = QColor(200,200,200,150);
    insideColor_ = QColor(200,200,200,0);
    dashColor_ = QColor(30,162,255,255);


    //虚线的样式
    dashPen_.setBrush(QBrush(dashColor_ , Qt::SolidPattern));
    qreal space = 2;
    dashes_ << 3 << space << 3 <<space;
    dashPen_.setDashPattern(dashes_);
    dashPen_.setWidth(sLineWidth_);


    //几个可以调整的点
    labelLeftBottom_ = new QLabel(this);
    labelLeftBottom_->setObjectName(QString::fromUtf8("labelLeftBottom"));
    labelLeftBottom_->setFixedSize(sDragDotWidth_,sDragDotWidth_);
    labelLeftBottom_->setCursor(QCursor(Qt::SizeBDiagCursor));
    labelLeftBottom_->setStyleSheet(QString::fromUtf8("background-color: rgb(30,162,255);"));
    labelRightBottom_ = new QLabel(this);
    labelRightBottom_->setObjectName(QString::fromUtf8("labelRightBottom"));
    labelRightBottom_->setFixedSize(sDragDotWidth_,sDragDotWidth_);
    labelRightBottom_->setCursor(QCursor(Qt::SizeFDiagCursor));
    labelRightBottom_->setStyleSheet(QString::fromUtf8("background-color: rgb(30,162,255);"));
    labelLeftTop_ = new QLabel(this);
    labelLeftTop_->setObjectName(QString::fromUtf8("labelLeftTop"));
    labelLeftTop_->setFixedSize(sDragDotWidth_,sDragDotWidth_);
    labelLeftTop_->setCursor(QCursor(Qt::SizeFDiagCursor));
    labelLeftTop_->setStyleSheet(QString::fromUtf8("background-color: rgb(30,162,255);"));
    labelRightTop_ = new QLabel(this);
    labelRightTop_->setObjectName(QString::fromUtf8("labelRightTop"));
    labelRightTop_->setFixedSize(sDragDotWidth_,sDragDotWidth_);
    labelRightTop_->setCursor(QCursor(Qt::SizeBDiagCursor));
    labelRightTop_->setStyleSheet(QString::fromUtf8("background-color: rgb(30,162,255);"));


    //事件监听
    labelLeftBottom_->installEventFilter(this);
    labelRightBottom_->installEventFilter(this);
    labelLeftTop_->installEventFilter(this);
    labelRightTop_->installEventFilter(this);


}


SelectRectWidget::~SelectRectWidget()
{
    labelLeftBottom_->removeEventFilter(this);
    labelRightBottom_->removeEventFilter(this);
    labelLeftTop_->removeEventFilter(this);
    labelRightTop_->removeEventFilter(this);
    delete ui;
}


void SelectRectWidget::checkCenterRect()
{
    centerRect_ = centerRect_.intersected(this->rect());
    if(centerRect_.bottom() >= this->rect().bottom()){
        centerRect_.setBottom(this->rect().bottom());
    }
    if(centerRect_.right() >= this->rect().right()){
        centerRect_.setRight(this->rect().right()-sLineWidth_);
    }
}


QRect SelectRectWidget::centerRect() const
{
    return centerRect_;
}


void SelectRectWidget::setSelectRectChange(const std::function<void (QRect rect)> &selectRectChange)
{
    selectRectChange_ = selectRectChange;
}


void SelectRectWidget::setFixCenterRectRatio(float ratio)
{
    fixCenterRectRatio_ = ratio;


    float widthBHeight = this->width()*1.0f/this->height();
    int tmpWidth = 0;
    int tmpHeight = 0;
    if(widthBHeight > ratio){
        tmpHeight = this->height();
        tmpWidth = ratio * tmpHeight;
    }else{
        tmpWidth = this->width();
        tmpHeight = tmpWidth/ratio;
    }


    //设置选择框值
    centerRect_.setRect((this->width() - tmpWidth)/2 , (this->height() - tmpHeight)/2 , tmpWidth , tmpHeight);
    this->update();
}


void SelectRectWidget::mousePressEvent(QMouseEvent *e)
{


    if(centerRect_.contains(e->pos(),true)){
        press_ = true;
        startPos_ = e->pos();
        dragMoveLocation_ = centerRect_;
    }
}


void SelectRectWidget::mouseMoveEvent(QMouseEvent *e)
{
    if (press_){
        QPoint movePos = e->pos()-startPos_;
        centerRect_.setRect(std::max(std::min(dragMoveLocation_.x()+movePos.x(), rect().width()-dragMoveLocation_.width()-sLineWidth_) , 0) ,
                            std::max(std::min(dragMoveLocation_.y()+movePos.y(), rect().height()-dragMoveLocation_.height()-sLineWidth_) , 0) ,
                            centerRect_.width() , centerRect_.height() );
        this->update();
    }
}


void SelectRectWidget::mouseReleaseEvent(QMouseEvent *e)
{
    press_ = false;
}
void SelectRectWidget::paintEvent(QPaintEvent *event)
{
    checkCenterRect();
    setRects();
    if(selectRectChange_ && (centerRect_ != preCenterRect_) ){
        selectRectChange_(centerRect_);
        preCenterRect_ = centerRect_;
    }
    QPainter painter(this);
    //绘制区域
    painter.fillRect(leftRect_, outsideColor_);
    painter.fillRect(topRect_, outsideColor_);
    painter.fillRect(rightRect_, outsideColor_);
    painter.fillRect(bottomRect_, outsideColor_);


    //绘制中间区域和边框虚线
//    dashPen_.setBrush(QBrush(dashColor_));
    painter.setPen(dashPen_);
    painter.fillRect(centerRect_, insideColor_);
    painter.drawRect(centerRect_);


    //move zoomdot
    labelLeftBottom_->move(centerRect_.x()-sDragDotWidth_/2+sDragDotOffset_,centerRect_.y()+centerRect_.height()-sDragDotWidth_/2-sDragDotOffset_);
    labelRightBottom_->move(centerRect_.x()+centerRect_.width()-sDragDotWidth_/2-sDragDotOffset_,centerRect_.y()+centerRect_.height()-sDragDotWidth_/2-sDragDotOffset_);
    labelLeftTop_->move(centerRect_.x()-sDragDotWidth_/2+sDragDotOffset_,centerRect_.y()-sDragDotWidth_/2+sDragDotOffset_);
    labelRightTop_->move(centerRect_.x()+centerRect_.width()-sDragDotWidth_/2-sDragDotOffset_,centerRect_.y()-sDragDotWidth_/2+sDragDotOffset_);




}


bool SelectRectWidget::eventFilter(QObject * obj, QEvent * e)
{
    if (obj == labelLeftBottom_
            || obj == labelLeftTop_
            || obj == labelRightBottom_
            || obj == labelRightTop_){
        //实现拖动右下角缩放窗口
        if(e->type() == QEvent::MouseButtonPress){
            QMouseEvent *event = (QMouseEvent *)e;
            // NoButton: fix frist press is NoButton bugs
            if (event->buttons() & Qt::LeftButton || event->buttons() == Qt::NoButton){
                dragZoomRunning_ = true;
                dragZoomLocation_ = centerRect_;
                dragZoomPos_ = event->globalPos();
                return true;
            }
        }
        else if(e->type() == QEvent::MouseMove){
            QMouseEvent *event = (QMouseEvent *)e;
            if (dragZoomRunning_ && (event->buttons() & Qt::LeftButton)){
                int dx = event->globalPos().x() - dragZoomPos_.x();
                int dy = event->globalPos().y() - dragZoomPos_.y();


                QRect rc = dragZoomLocation_;
                if (obj == labelLeftBottom_){
                    if(fixCenterRectRatio_ > 0){
                        if(std::abs(dx) > std::abs(dy)){
                            centerRect_.setLeft(std::max(rc.left() + dx , 0));
                            if(centerRect_.left()+sCenterRectMinWidth_ > centerRect_.right()){
                                centerRect_.setLeft(centerRect_.right() - sCenterRectMinWidth_);
                            }
                            centerRect_.setBottom(rc.top() + centerRect_.width()/fixCenterRectRatio_);
                            if(centerRect_.bottom() > this->height()-sLineWidth_){
                                centerRect_.setBottom(this->height() - sLineWidth_);
                                centerRect_.setLeft(rc.right() - centerRect_.height()*fixCenterRectRatio_);
                            }
                        }else{
                            centerRect_.setBottom(std::min(rc.bottom() + dy , this->height()-sLineWidth_));
                            if(centerRect_.bottom()-sCenterRectMinWidth_ < centerRect_.top()){
                                centerRect_.setBottom(rc.top() + sCenterRectMinWidth_);
                            }
                            centerRect_.setLeft(rc.right() - centerRect_.height()*fixCenterRectRatio_);
                            if(centerRect_.left() < 0){
                                centerRect_.setLeft(0);
                                centerRect_.setBottom(rc.top() + centerRect_.width()/fixCenterRectRatio_);
                            }
                        }
                    }else{
                        centerRect_.setLeft(rc.left() + dx);
                        centerRect_.setBottom(rc.bottom() + dy);
                        if(centerRect_.left()+sCenterRectMinWidth_ > centerRect_.right()){
                            centerRect_.setLeft(centerRect_.right() - sCenterRectMinWidth_);
                        }
                        if(centerRect_.bottom()-sCenterRectMinWidth_ < centerRect_.top()){
                            centerRect_.setBottom(centerRect_.top() + sCenterRectMinWidth_);
                        }
                    }


                }else if (obj == labelLeftTop_){
                    if(fixCenterRectRatio_ > 0){
                        if(std::abs(dx) > std::abs(dy)){
                            centerRect_.setLeft(std::max(rc.left() + dx , 0));
                            if(centerRect_.left()+sCenterRectMinWidth_ > centerRect_.right()){
                                centerRect_.setLeft(centerRect_.right() - sCenterRectMinWidth_);
                            }
                            centerRect_.setTop(rc.bottom() - centerRect_.width()/fixCenterRectRatio_);
                            if(centerRect_.top() < 0){
                                centerRect_.setTop(0);
                                centerRect_.setLeft(rc.right() - centerRect_.height()*fixCenterRectRatio_);
                            }
                        }else{
                            centerRect_.setTop(std::max(rc.top() + dy , 0));
                            if(centerRect_.bottom()-sCenterRectMinWidth_ < centerRect_.top()){
                                centerRect_.setTop(rc.bottom() - sCenterRectMinWidth_);
                            }
                            centerRect_.setLeft(rc.right() - centerRect_.height()*fixCenterRectRatio_);
                            if(centerRect_.left() < 0){
                                centerRect_.setLeft(0);
                                centerRect_.setTop(rc.bottom() - centerRect_.width()/fixCenterRectRatio_);
                            }
                        }
                    }else{
                        centerRect_.setLeft(rc.left() + dx);
                        centerRect_.setTop(rc.top() + dy);
                        if(centerRect_.left()+sCenterRectMinWidth_ > centerRect_.right()){
                            centerRect_.setLeft(centerRect_.right() - sCenterRectMinWidth_);
                        }
                        if(centerRect_.bottom()-sCenterRectMinWidth_ < centerRect_.top()){
                            centerRect_.setTop(centerRect_.bottom() - sCenterRectMinWidth_);
                        }
                    }


                }else if (obj == labelRightBottom_){
                    if(fixCenterRectRatio_ > 0){
                        if(std::abs(dx) > std::abs(dy)){
                            centerRect_.setRight(std::min(rc.right() + dx , this->width()-sLineWidth_));
                            if(centerRect_.left()+sCenterRectMinWidth_ > centerRect_.right()){
                                centerRect_.setRight(centerRect_.left() + sCenterRectMinWidth_);
                            }
                            centerRect_.setBottom(rc.top() + centerRect_.width()/fixCenterRectRatio_);
                            if(centerRect_.bottom() > this->height()-sLineWidth_){
                                centerRect_.setBottom(this->height() - sLineWidth_);
                                centerRect_.setRight(rc.left() + centerRect_.height()*fixCenterRectRatio_);
                            }
                        }else{
                            centerRect_.setBottom(std::min(rc.bottom() + dy , this->height()-sLineWidth_));
                            if(centerRect_.bottom()-sCenterRectMinWidth_ < centerRect_.top()){
                                centerRect_.setBottom(rc.top() + sCenterRectMinWidth_);
                            }
                            centerRect_.setRight(rc.left() + centerRect_.height()*fixCenterRectRatio_);
                            if(centerRect_.right() > this->width()-sLineWidth_){
                                centerRect_.setRight(this->width()-sLineWidth_);
                                centerRect_.setBottom(rc.top() + centerRect_.width()/fixCenterRectRatio_);
                            }
                        }
                    }else{
                        centerRect_.setRight(rc.right() + dx);
                        centerRect_.setBottom(rc.bottom() + dy);
                        if(centerRect_.left()+sCenterRectMinWidth_ > centerRect_.right()){
                            centerRect_.setRight(centerRect_.left() + sCenterRectMinWidth_);
                        }
                        if(centerRect_.bottom()-sCenterRectMinWidth_ < centerRect_.top()){
                            centerRect_.setBottom(centerRect_.top() + sCenterRectMinWidth_);
                        }
                    }


                }else if (obj == labelRightTop_){
                    if(fixCenterRectRatio_ > 0){
                        if(std::abs(dx) > std::abs(dy)){
                            centerRect_.setRight(std::min(rc.right() + dx , this->width()-sLineWidth_));
                            if(centerRect_.left()+sCenterRectMinWidth_ > centerRect_.right()){
                                centerRect_.setRight(centerRect_.left() + sCenterRectMinWidth_);
                            }
                            centerRect_.setTop(rc.bottom() - centerRect_.width()/fixCenterRectRatio_);
                            if(centerRect_.top() < 0){
                                centerRect_.setTop(0);
                                centerRect_.setRight(rc.left() + centerRect_.height()*fixCenterRectRatio_);
                            }
                        }else{
                            centerRect_.setTop(std::max(rc.top() + dy , 0));
                            if(centerRect_.bottom()-sCenterRectMinWidth_ < centerRect_.top()){
                                centerRect_.setTop(rc.bottom() - sCenterRectMinWidth_);
                            }
                            centerRect_.setRight(rc.left() + centerRect_.height()*fixCenterRectRatio_);
                            if(centerRect_.right() > this->width()-sLineWidth_){
                                centerRect_.setRight(this->width()-sLineWidth_);
                                centerRect_.setTop(rc.bottom() - centerRect_.width()/fixCenterRectRatio_);
                            }
                        }
                    }else{
                        centerRect_.setRight(rc.right() + dx);
                        centerRect_.setTop(rc.top() + dy);
                        if(centerRect_.left()+sCenterRectMinWidth_ > centerRect_.right()){
                            centerRect_.setRight(centerRect_.left() + sCenterRectMinWidth_);
                        }
                        if(centerRect_.bottom()-sCenterRectMinWidth_ < centerRect_.top()){
                            centerRect_.setTop(centerRect_.bottom() - sCenterRectMinWidth_);
                        }
                    }
                }
                this->update();
                return true;
            }
        }
        else if(e->type() == QEvent::MouseButtonRelease){
            dragZoomRunning_ = false;
            return true;
        }
    }
    return QWidget::eventFilter(obj, e);
}


调用

因为有小伙伴私信咨询调用方法,所以这里把调用的代码放上

	//ui->widgetOriginArea就是ui文件里的SelectRectWidget控件
	//设置大小,因为要覆盖底部的图片控件,所以直接使用图片的控件的位置和大小
    ui->widgetOriginArea->setGeometry(ui->widgetCutContainer->imgRenderRect());
    //设置选择框固定比例
    ui->widgetOriginArea->setFixCenterRectRatio(ratio);
    //拖动选择框时的选中区域回调
    ui->widgetOriginArea->setSelectRectChange([this](QRect rect){
        if(ui->widgetAdvice_43->isHidden()){
            return ;
        }
        QImage image = ui->widgetCutContainer->cutShowImage(rect);
        ui->widgetAdvice_43->showImage(image);
        ui->widgetAdvice_169->showImage(image);
    });


//最后点击上传后,根据ui->widgetOriginArea->centerRect()获取中间选中区域的Rect来截取图片
cutShowImage_ = ui->widgetCutContainer->cutShowImage(ui->widgetOriginArea->centerRect());
  • 6
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值