之前做了一个项目中,需要画取ROI来进行图片区域选择,因为以前没有搞过就研究了一下。
一开始想着是在界面加载一个QLabel来加载图像,然后直接画一个ROI出来,结果画的框总是在控件下面没解决。后来看到有篇博客是在一个自定义的QLabel中实现, 重写实现它的一些鼠标事件绘图事件等。计算好相应的位置值即可。
本文和该功能的实现具体借鉴了两篇博客文章的而实现:1 QT-绘制ROI、矩形框、椭圆框,机器视觉_Create_Joy的博客-CSDN博客_qt绘制矩形框 2 qt+opencv实现视频中动态选取ROI_给算法爸爸上香的博客-CSDN博客
本文作者原创,未经允许禁止转载。
QT ROI 绘制区域目录
1 效果图
1.1 示例1
该文章实现了画ROI的功能,可以画矩形、椭圆;
1.2 示例2
实现了画ROI 拖动功能;
1.3 示例3
最后实现的效果图,可以看出正常画ROI均正常,也可以输出具体的XYWH信息。
2 具体代码
这里没有附上完整版本代码,因为太多了。 文章结尾会提供CSDN下载链接,供下载参考。
2.1 示例1源码
///DrawQWidget.h
#ifndef DRAWQWIDGET_H
#define DRAWQWIDGET_H
#include <QWidget>
#include <QKeyEvent>
#include <qpoint.h>
#include <qpen.h>
/*
*
*/
typedef enum draw_shap_e{
DRAW_RECT, //画矩形
DRAW_ELLIPSE, //画椭圆
DRAW_NO //不画
}DRAW_SHAP_E;
/* 用来表示鼠标在矩形区域的位置信息
*
*/
typedef enum rect_mouse_position_e{
RECT_UPPER=0, //上边缘
RECT_LOWER=1, //下边缘
RECT_LEFT, //左边缘
RECT_RIGHT, //右边缘
RECT_LEFTUPPER, //左上角
RECT_LEFTLOWER, //左下角
RECT_RIGHTLOWER, //右下角
RECT_RIGHTUPPER, //右上角
RECT_INSIDE, //区域内部
RECT_OUTSIDE //区域外部
}RECT_MOUSE_POSITION_E;
/* 用来表示鼠标在椭圆区域的位置信息
*
*/
typedef enum ellipse_mouse_position_e{
ELLIPSE_UPPER=0, //上顶角
ELLIPSE_LOWER=1, //下顶角
ELLIPSE_LEFT, //左顶角
ELLIPSE_RIGHT, //右顶角
ELLIPSE_INSIDE, //区域内部
ELLIPSE_OUTSIDE //区域外部
}ELLIPSE_MOUSE_POSITION_E;
class DrawQWidget : public QWidget
{
Q_OBJECT
public:
explicit DrawQWidget(QWidget *parent = 0);
~DrawQWidget();
void set_draw_shap(DRAW_SHAP_E d);
void set_picture_image(QString file_name);
protected:
void timerEvent(QTimerEvent*);
void paintEvent(QPaintEvent*) override;
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
private:
bool is_mouse_pressed;//是否按下鼠标
DRAW_SHAP_E draw_shap;
QPoint new_mouse_pos;
QPoint old_mouse_pos;
int m_difference_x;
int m_difference_y;
QPainter *painter; //用来绘制图像
QPen frame_pen; //用来绘制区域边框
QPen red_point_pen; //用来绘制红色点
const int BoundaryRange = 6;//用来表示边界的宽度范围,用于拖拽
/* 矩形区域相关变量和函数
* 注意:QPoint的0点是左上角,横向向右为X的正方向,竖向向下为Y的正方向
*/
int rect_left; //表示矩形右上角的X坐标
int rect_top; //表示矩形右上角的Y坐标
int rect_width; //表示矩形的宽(即水平长度)
int rect_height; //表示矩形的高(即垂直长度)
int rect_top_left_x, rect_top_left_y; //左上
int rect_top_right_x, rect_top_right_y; //右上
int rect_low_left_x, rect_low_left_y; //左下
int rect_low_right_x, rect_low_right_y; //右下
QPolygon rect_polygon; //装载8个红点的坐标
RECT_MOUSE_POSITION_E rect_mouse_pos;
void rect_init_region();
void rect_update_region();
void rect_change_region();
RECT_MOUSE_POSITION_E rect_get_mouse_pos(int pos_x, int pos_y);
/* 椭圆区域相关变量和函数
* 注意:drawEllipse(20,20,210,160);
* 第1,2个参数表示圆/椭圆距屏幕左上角的像素数,第3,4个参数表示圆/椭圆的宽度和高度。
* 更加确切地表述,这个圆或椭圆是在矩形中,这个矩形的左上角的顶点在坐标轴中的位置为(20,20),
* 这个圆或椭圆的中心为这个矩形的中心
*/
int ellipse_left; //表示椭圆右上角的X坐标
int ellipse_top; //表示椭圆右上角的Y坐标
int ellipse_width; //表示椭圆的宽(即水平长度)
int ellipse_height; //表示椭圆的高(即垂直长度)
int ellipse_middle_x;
int ellipse_middle_y;
QPolygon ellipse_polygon; //装载4个红点的坐标
ELLIPSE_MOUSE_POSITION_E ellipse_mouse_pos;
void ellipse_init_region();
void ellipse_update_region();
void ellipse_change_region();
ELLIPSE_MOUSE_POSITION_E ellipse_get_mouse_pos(int pos_x, int pos_y);
/* other
*
*/
int timer_id;
QImage picture_image;
int picture_image_w;
int picture_image_h;
public slots:
};
#endif // DRAWQWIDGET_H
///DrawQWidget.cpp
#include "DrawQWidget.h"
#include <qdebug.h>
#include <qpainter.h>
DrawQWidget::DrawQWidget(QWidget *parent) : QWidget(parent)
{
setAttribute(Qt::WA_StyledBackground);
setStyleSheet("background-color: rgb(0, 0, 0);");
grabKeyboard();
setMouseTracking(true);
m_difference_x = 0;
m_difference_y = 0;
draw_shap = DRAW_NO;
painter = new QPainter(this);
frame_pen = QPen(QColor(0,174,255),2);
red_point_pen = QPen(QColor(255,0,0),4);
is_mouse_pressed = false;
timer_id = startTimer(20);
rect_init_region();
ellipse_init_region();
/// 开启鼠标实时追踪
setMouseTracking(true);
}
DrawQWidget::~DrawQWidget()
{
killTimer(timer_id);
}
/* Event function
*
*/
void DrawQWidget::timerEvent(QTimerEvent *)
{
this->update();
}
void DrawQWidget::paintEvent(QPaintEvent *)
{
painter->begin(this);
painter->drawImage(QRectF(0,0,width(),height()), picture_image);
switch (draw_shap) {
case (DRAW_RECT) :{
painter->setPen(frame_pen);//绘制边框线
painter->drawRect(QRect(rect_left, rect_top, rect_width, rect_height));
painter->setPen(red_point_pen);//绘制八个点
painter->drawPoints(rect_polygon);
}break;
case (DRAW_ELLIPSE) :{
painter->setPen(frame_pen);//绘制边框线
painter->drawEllipse(QRect(ellipse_left, ellipse_top, ellipse_width, ellipse_height));
painter->setPen(red_point_pen);//绘制四个点
painter->drawPoints(ellipse_polygon);
}break;
case (DRAW_NO) :break;
}
painter->end();
}
void DrawQWidget::mousePressEvent(QMouseEvent *event)
{
// if (!is_start_draw) return;
is_mouse_pressed = true;
}
void DrawQWidget::mouseMoveEvent(QMouseEvent *event)
{
// if (!is_start_draw) return;
new_mouse_pos = event->pos();
if (is_mouse_pressed) {
m_difference_x = new_mouse_pos.x() - old_mouse_pos.x();
m_difference_y = new_mouse_pos.y() - old_mouse_pos.y();
switch (draw_shap) {
case (DRAW_RECT) :rect_change_region();break;
case (DRAW_ELLIPSE) :ellipse_change_region();break;
case (DRAW_NO) :break;
}
}else{
switch (draw_shap) {
case (DRAW_RECT) :rect_mouse_pos = rect_get_mouse_pos(new_mouse_pos.x(), new_mouse_pos.y());break;
case (DRAW_ELLIPSE) :ellipse_mouse_pos = ellipse_get_mouse_pos(new_mouse_pos.x(), new_mouse_pos.y());break;
case (DRAW_NO) :break;
}
}
old_mouse_pos = new_mouse_pos;
}
void DrawQWidget::mouseReleaseEvent(QMouseEvent *event)
{
// if (!is_start_draw) return;
is_mouse_pressed = false;
}
/* Rect function
*
*/
void DrawQWidget::rect_init_region()
{
rect_left = 100;
rect_top = 200;
rect_width = 101;
rect_height = 101;
rect_mouse_pos = RECT_OUTSIDE;
rect_update_region();
}
void DrawQWidget::rect_update_region()
{
rect_top_left_x = rect_left; rect_top_left_y = rect_top;
rect_top_right_x = rect_left+rect_width; rect_top_right_y = rect_top;
rect_low_left_x = rect_left; rect_low_left_y = rect_top+rect_height;
rect_low_right_x = rect_left+rect_width; rect_low_right_y = rect_top+rect_height;
int Middle_X = rect_left + (rect_width>>1);
int Middle_Y = rect_top + (rect_height>>1);
rect_polygon.clear();
rect_polygon<<QPoint(Middle_X, rect_top) //上中
<<QPoint(rect_top_right_x, Middle_Y) //右中
<<QPoint(Middle_X, rect_low_left_y) //下中
<<QPoint(rect_left, Middle_Y) //左中
<<QPoint(rect_left, rect_top) //左上角
<<QPoint(rect_top_right_x, rect_top) //右上角
<<QPoint(rect_top_right_x, rect_low_left_y) //右下角
<<QPoint(rect_left, rect_low_left_y); //左下角
}
void DrawQWidget::rect_change_region()
{
switch (rect_mouse_pos) {
case (RECT_UPPER): rect_top += m_difference_y; rect_height -= m_difference_y;break;//上边界
case (RECT_LOWER): rect_height += m_difference_y;break; //下边界
case (RECT_LEFT) : rect_left += m_difference_x; rect_width -= m_difference_x;break;//左边界
case (RECT_RIGHT): rect_width += m_difference_x;break; //右边界
case (RECT_LEFTUPPER) : {//左上角
rect_top += m_difference_y; rect_height -= m_difference_y;
rect_left += m_difference_x; rect_width -= m_difference_x;
}break;
case (RECT_LEFTLOWER) : {//左下角
rect_height += m_difference_y;
rect_left += m_difference_x; rect_width -= m_difference_x;
}break;
case (RECT_RIGHTLOWER) : {//右下角
rect_height += m_difference_y;
rect_width += m_difference_x;
}break;
case (RECT_RIGHTUPPER) : {//右上角
rect_top += m_difference_y; rect_height -= m_difference_y;
rect_width += m_difference_x;
}break;
case (RECT_INSIDE) : {//内部
rect_top += m_difference_y;rect_left += m_difference_x;
}break;
case (RECT_OUTSIDE) : return;//外部
}
rect_update_region();
}
RECT_MOUSE_POSITION_E DrawQWidget::rect_get_mouse_pos(int pos_x, int pos_y)
{
if (pos_x < rect_top_left_x || pos_x > rect_top_right_x || pos_y < rect_top_left_y || pos_y > rect_low_left_y) {
this->setCursor(QCursor(Qt::ArrowCursor));
return RECT_OUTSIDE;
}else if (pos_y <= rect_top_left_y+BoundaryRange){ //1:左上角 2:右上角 3:上边缘
if (pos_x <= rect_top_left_x+BoundaryRange) {this->setCursor(QCursor(Qt::SizeFDiagCursor));return RECT_LEFTUPPER;}
else if (pos_x >= rect_top_right_x-BoundaryRange){this->setCursor(QCursor(Qt::SizeBDiagCursor));return RECT_RIGHTUPPER;}
else {this->setCursor(QCursor(Qt::SizeVerCursor)); return RECT_UPPER;}
}else if (pos_y >= rect_low_left_y-BoundaryRange){ //1:左下角 2:右下角 3:下边缘
if (pos_x <= rect_low_left_x+BoundaryRange) {this->setCursor(QCursor(Qt::SizeBDiagCursor));return RECT_LEFTLOWER;}
else if (pos_x >= rect_low_right_x-BoundaryRange){this->setCursor(QCursor(Qt::SizeFDiagCursor));return RECT_RIGHTLOWER;}
else {this->setCursor(QCursor(Qt::SizeVerCursor)); return RECT_LOWER;}
}else if (pos_x <= rect_top_left_x+BoundaryRange) { //左边缘
this->setCursor(QCursor(Qt::SizeHorCursor)); return RECT_LEFT;
}else if (pos_x >= rect_top_right_x-BoundaryRange) { //右边缘
this->setCursor(QCursor(Qt::SizeHorCursor)); return RECT_RIGHT;
}else {
this->setCursor(QCursor(Qt::SizeAllCursor));
return RECT_INSIDE;
}
}
/* Ellipse function
*
*/
void DrawQWidget::ellipse_init_region()
{
ellipse_left = 100;
ellipse_top = 200;
ellipse_width = 101;
ellipse_height = 101;
ellipse_mouse_pos = ELLIPSE_OUTSIDE;
ellipse_update_region();
}
void DrawQWidget::ellipse_update_region()
{
ellipse_middle_x = ellipse_left + (ellipse_width>>1);
ellipse_middle_y = ellipse_top + (ellipse_height>>1);
ellipse_polygon.clear();
ellipse_polygon<<QPoint(ellipse_middle_x, ellipse_top) //上顶角
<<QPoint(ellipse_left+ellipse_width, ellipse_middle_y) //右顶角
<<QPoint(ellipse_middle_x, ellipse_top+ellipse_height) //下顶角
<<QPoint(ellipse_left, ellipse_middle_y); //左顶角
}
void DrawQWidget::ellipse_change_region()
{
switch (ellipse_mouse_pos) {
case (ELLIPSE_UPPER) : ellipse_top += m_difference_y; ellipse_height -= m_difference_y;break;
case (ELLIPSE_LOWER) : ellipse_height += m_difference_y;break;
case (ELLIPSE_LEFT) : ellipse_left += m_difference_x; ellipse_width -= m_difference_x;break;
case (ELLIPSE_RIGHT) : ellipse_width += m_difference_x;break;
case (ELLIPSE_INSIDE) : ellipse_top += m_difference_y;ellipse_left += m_difference_x;break;
case (ELLIPSE_OUTSIDE): return;
}
ellipse_update_region();
}
ELLIPSE_MOUSE_POSITION_E DrawQWidget::ellipse_get_mouse_pos(int pos_x, int pos_y)
{
if (pos_x < ellipse_left || pos_x > (ellipse_left+ellipse_width) || pos_y < ellipse_top || pos_y > (ellipse_top+ellipse_height)) {
this->setCursor(QCursor(Qt::ArrowCursor));
return ELLIPSE_OUTSIDE;
}else if (pos_y <= ellipse_top+BoundaryRange){ //上顶角
if (pos_x >= (ellipse_middle_x-3) && pos_x <= (ellipse_middle_x+3)){
this->setCursor(QCursor(Qt::SizeVerCursor)); return ELLIPSE_UPPER;
}else{
this->setCursor(QCursor(Qt::SizeAllCursor)); return ELLIPSE_INSIDE;
}
}else if (pos_y >= ellipse_top+ellipse_height-BoundaryRange){ //下顶角
if (pos_x >= (ellipse_middle_x-3) && pos_x <= (ellipse_middle_x+3)) {
this->setCursor(QCursor(Qt::SizeVerCursor)); return ELLIPSE_LOWER;
}else{
this->setCursor(QCursor(Qt::SizeAllCursor)); return ELLIPSE_INSIDE;
}
}else if (pos_x <= ellipse_left+BoundaryRange) { //左顶角
if (pos_y >= (ellipse_middle_y-3) && pos_y <= (ellipse_middle_y+3)) {
this->setCursor(QCursor(Qt::SizeHorCursor)); return ELLIPSE_LEFT;
}else{
this->setCursor(QCursor(Qt::SizeAllCursor)); return ELLIPSE_INSIDE;
}
}else if (pos_x >= ellipse_left+ellipse_width-BoundaryRange) { //右顶角
if (pos_y >= (ellipse_middle_y-3) && pos_y <= (ellipse_middle_y+3)) {
this->setCursor(QCursor(Qt::SizeHorCursor)); return ELLIPSE_RIGHT;
}else{
this->setCursor(QCursor(Qt::SizeAllCursor)); return ELLIPSE_INSIDE;
}
}else {
this->setCursor(QCursor(Qt::SizeAllCursor));
return ELLIPSE_INSIDE;
}
}
/* other function
*
*/
void DrawQWidget::set_draw_shap(DRAW_SHAP_E d)
{
draw_shap = d;
}
void DrawQWidget::set_picture_image(QString file_name)
{
QImage image_tmp;
image_tmp.load(file_name);
if (!image_tmp.isNull()){
picture_image = image_tmp;
picture_image_w = image_tmp.width();
picture_image_h = image_tmp.height();
}
}
2.2 示例2源码
///MyLabel.h
#pragma once
#include <QLabel>
/* 缩放方向 */
enum EmDirection
{
DIR_TOP = 0,
DIR_BOTTOM,
DIR_LEFT,
DIR_RIGHT,
DIR_LEFTTOP,
DIR_LEFTBOTTOM,
DIR_RIGHTTOP,
DIR_RIGHTBOTTOM,
DIR_MIDDLE,
DIR_NONE
};
#define EDGPADDING 5 //四周边缘可拉伸宽度
#define CORPADDING 6 //四角可拉伸宽度
#define MIN_WIDTH 5 //可拉伸的最小宽度
#define MIN_HEIGHT 5 //可拉伸的最小高度
#define POINT_WIDTH 6 //边缘9点的宽度
#define POINT_HEIGHT 6 //边缘9点的高度
#define EDGE_WIDTH 3 //边框的宽度
#define MIDDLELINE_WIDTH 2 //辅助线的宽度
#define DRAW_TEN_POINT //绘制十个点
#define DRAW_SUB_LINE //绘制辅助线
class QMenu;
class QAction;
class MyLabel : public QLabel
{
Q_OBJECT
public:
MyLabel(QWidget *parent = nullptr);
~MyLabel();
QRect getRoiRect() const; //获取已经圈选的框 外部调用
void setBackImage(const QImage &img); //设置背景图片 外部调用
protected:
void paintEvent(QPaintEvent *event);
void mousePressEvent(QMouseEvent *ev);
void mouseMoveEvent(QMouseEvent *ev);
void mouseReleaseEvent(QMouseEvent *ev);
void keyPressEvent(QKeyEvent *ev);
void contextMenuEvent(QContextMenuEvent *ev);
private:
void initViewer(); //初始化
void saveROIImage(); //把ROI区域的图片存储下来
EmDirection region(const QPoint &point); //根据鼠标位置设置鼠标形状
void scaleRect(const QPoint &mousePoint); //缩放矩形
void paintRect(const QPoint &mousePoint); //绘制矩形
void moveRect(const QPoint &mousePoint); //移动矩形
private:
bool m_bPainterPressed; //是否正在绘制
bool m_bMovedPressed; //是否正在拖动
bool m_bScalePressed; //是否正在缩放大小
QPoint m_paintStartPoint; //绘制的初始位置
QPoint m_moveStartPoint; //拖动的初始位置
QRect m_roiRect; //绘制的ROI
EmDirection m_emCurDir; //拖动的方向
QImage m_backImage; //背景图
QMenu *m_pOptMenu;
QAction *m_pDelAction;
QAction *m_pSaveAction;
};
///MyLabel.cpp
#include "MyLabel.h"
#include <QPainter>
#include <QMouseEvent>
#include <QMenu>
#include <QAction>
#include <QStandardPaths>
#include <QFileDialog>
#include <QDebug>
MyLabel::MyLabel(QWidget *parent)
: QLabel(parent)
{
this->initViewer();
}
MyLabel::~MyLabel()
{
}
void MyLabel::initViewer()
{
m_bPainterPressed = false;
m_bMovedPressed = false;
m_bScalePressed = false;
m_roiRect = QRect(0, 0, 0, 0);
m_emCurDir = EmDirection::DIR_NONE;
this->setMouseTracking(true);
this->setFocusPolicy(Qt::StrongFocus);
m_pOptMenu = new QMenu(this);
m_pDelAction = new QAction(QStringLiteral("删除"), this);
connect(m_pDelAction, &QAction::triggered, this, [&]() { m_roiRect = QRect(0, 0, 0, 0); });
m_pSaveAction = new QAction(QStringLiteral("保存"), this);
connect(m_pSaveAction, &QAction::triggered, this, &MyLabel::saveROIImage);
m_pOptMenu->addAction(m_pDelAction);
m_pOptMenu->addAction(m_pSaveAction);
}
/**
* @brief 保存roi图形
*
* @return void
*/
void MyLabel::saveROIImage()
{
QString fileName = QFileDialog::getSaveFileName(this, "Save", QStandardPaths::writableLocation(QStandardPaths::PicturesLocation), "*.png");
if (fileName.isEmpty())
{
return;
}
QImage saveImg = m_backImage.copy(m_roiRect);
saveImg.save(fileName);
}
/**
* @brief 获取绘制的矩形
*
* @return 绘制的矩形
*/
QRect MyLabel::getRoiRect() const
{
return m_roiRect;
}
/**
* @brief 设置背景图片
* @param img QImage图片
*
* @return void
*/
void MyLabel::setBackImage(const QImage & img)
{
m_backImage = img;
this->setMinimumSize(img.width(), img.height());
update();
}
/**
* @brief 绘制矩形
* @param event 绘图事件
*
* @return void
*/
void MyLabel::paintEvent(QPaintEvent * event)
{
#if 1
QLabel::paintEvent(event);
if (m_backImage.isNull())
return;
QPixmap rawImg = QPixmap::fromImage(m_backImage);
QString strPoint = QString("X:%0, Y:%1").arg(m_roiRect.x()).arg(m_roiRect.y()); //位置信息
QString strSize = QString("W:%0, H:%1").arg(m_roiRect.width()).arg(m_roiRect.height()); //大小信息
QPen pen;
pen.setColor(Qt::yellow);
pen.setWidth(EDGE_WIDTH);
QFont font;
font.setPixelSize(16);
QPainter painter;
painter.begin(this);
painter.setBrush(QBrush(QColor(0, 0, 200, 120)));
painter.setPen(pen);
painter.setFont(font);
painter.drawPixmap(0, 0, rawImg);
painter.drawText(m_roiRect.topLeft().x(), m_roiRect.topLeft().y() - 5, strSize);
painter.drawText(m_roiRect.topLeft().x(), m_roiRect.topLeft().y() - 18, strPoint);
painter.drawRect(m_roiRect);
if (m_roiRect.width() != 0 && m_roiRect.height() != 0)
{
#ifdef DRAW_SUB_LINE
//绘制中间十字架
QPen dashPen(Qt::white);
dashPen.setWidth(MIDDLELINE_WIDTH);
dashPen.setStyle(Qt::DashDotLine);
painter.setPen(dashPen);
painter.drawLine(m_roiRect.topLeft().x() + m_roiRect.width() / 2, m_roiRect.topLeft().y() + EDGE_WIDTH, m_roiRect.bottomRight().x() - m_roiRect.width() / 2, m_roiRect.bottomRight().y());
painter.drawLine(m_roiRect.topLeft().x() + EDGE_WIDTH, m_roiRect.topLeft().y() + m_roiRect.height() / 2, m_roiRect.bottomRight().x(), m_roiRect.bottomRight().y() - m_roiRect.height() / 2);
#endif
#ifdef DRAW_TEN_POINT
//绘制边缘十个点
painter.setBrush(Qt::green);
pen.setWidth(0);
painter.setPen(pen);
painter.drawRect(m_roiRect.topLeft().x(), m_roiRect.topLeft().y(), POINT_WIDTH, POINT_HEIGHT); //左上角
painter.drawRect(m_roiRect.topLeft().x(), m_roiRect.topLeft().y() + m_roiRect.height() / 2 - POINT_WIDTH / 2, POINT_WIDTH, POINT_HEIGHT); //左边中心点
painter.drawRect(m_roiRect.bottomLeft().x(), m_roiRect.bottomLeft().y()- POINT_WIDTH, POINT_WIDTH, POINT_HEIGHT); //左下角
painter.drawRect(m_roiRect.topLeft().x() + m_roiRect.width() / 2 - POINT_WIDTH / 2, m_roiRect.topLeft().y(), POINT_WIDTH, POINT_HEIGHT); //顶部中心
painter.drawRect(m_roiRect.topLeft().x() + m_roiRect.width() / 2 - POINT_WIDTH /2, m_roiRect.topLeft().y() + m_roiRect.height() / 2 - POINT_WIDTH / 2, POINT_WIDTH, POINT_HEIGHT); //中心点
painter.drawRect(m_roiRect.bottomLeft().x() + m_roiRect.width() / 2 - POINT_WIDTH / 2, m_roiRect.bottomLeft().y() - POINT_WIDTH, POINT_WIDTH, POINT_HEIGHT); //底部中心点
painter.drawRect(m_roiRect.topRight().x() - POINT_WIDTH, m_roiRect.topRight().y(), POINT_WIDTH, POINT_HEIGHT); //右上角
painter.drawRect(m_roiRect.topRight().x() - POINT_WIDTH / 2, m_roiRect.topRight().y() + m_roiRect.height() / 2 - POINT_WIDTH /2, POINT_WIDTH, POINT_HEIGHT); //右边中心点
painter.drawRect(m_roiRect.bottomRight().x() - POINT_WIDTH, m_roiRect.bottomRight().y() - POINT_WIDTH, POINT_WIDTH, POINT_HEIGHT); //右下角点
#endif
}
painter.end();
qDebug() << m_roiRect;
#else
QLabel::paintEvent(event);
if (m_backImage.isNull())
return;
QPixmap rawImg = QPixmap::fromImage(m_backImage);
QPainter painter(this);
painter.begin(&rawImg);
painter.setBrush(Qt::gray);
painter.drawRect(30,30,100,100);
painter.end();
this->setPixmap(rawImg);
#endif
}
/**
* @brief 鼠标按下事件 用于拖动绘制缩放起始
* @param ev 鼠标事件
*
* @return void
*/
void MyLabel::mousePressEvent(QMouseEvent * ev)
{
if (ev->buttons() & Qt::LeftButton)
{
EmDirection dir = region(ev->pos()); //获取鼠标当前的位置
if (dir == DIR_MIDDLE)
{
//鼠标在矩形中心位置
this->setCursor(Qt::ClosedHandCursor);
m_moveStartPoint.setX(ev->pos().x());
m_moveStartPoint.setY(ev->pos().y());
m_bMovedPressed = true;
}
else if (dir == DIR_NONE)
{
//鼠标在矩形外部
this->setCursor(Qt::ArrowCursor);
m_bPainterPressed = true;
m_paintStartPoint.setX(ev->pos().x());
m_paintStartPoint.setY(ev->pos().y());
}
else
{
//矩形在矩形边缘
m_moveStartPoint.setX(ev->pos().x());
m_moveStartPoint.setY(ev->pos().y());
m_bScalePressed = true;
m_emCurDir = dir;
}
}
}
/**
* @brief 鼠标移动事件
* @param ev 鼠标事件
*
* @return void
*/
void MyLabel::mouseMoveEvent(QMouseEvent * ev)
{
if (ev->buttons() & Qt::LeftButton)
{
if (m_bPainterPressed)
{
//正在绘制状态
paintRect(ev->pos());
}
else if (m_bMovedPressed)
{
//正在移动状态
moveRect(ev->pos());
}
else if (m_bScalePressed)
{
//正在缩放大小状态
scaleRect(ev->pos());
}
//更新界面
update();
return;
}
//根据鼠标的位置设置当前的鼠标形状
EmDirection dir = region(ev->pos());
if (dir == DIR_NONE)
{
setCursor(Qt::ArrowCursor);
}
else if (dir == DIR_MIDDLE)
{
setCursor(Qt::OpenHandCursor);
}
}
/**
* @brief 鼠标松开事件
* @param ev 鼠标事件
*
* @return void
*/
void MyLabel::mouseReleaseEvent(QMouseEvent * ev)
{
//判断鼠标是否在矩形中
if (m_roiRect.contains(ev->pos()))
{
//松开鼠标前是否正在拖放
if (m_bMovedPressed)
{
this->setCursor(Qt::OpenHandCursor);
}
else
{
this->setCursor(Qt::ArrowCursor);
}
}
m_paintStartPoint = QPoint();
m_bMovedPressed = false;
m_bPainterPressed = false;
m_bScalePressed = false;
}
/**
* @brief 键盘按下事件
* @param ev 键盘事件
*
* @return void
*/
void MyLabel::keyPressEvent(QKeyEvent * ev)
{
if (ev->key() == Qt::Key_Delete)
{
m_roiRect = QRect(0,0,0,0);
update();
}
}
/**
* @brief 右键菜单
* @param ev 菜单事件
*
* @return void
*/
void MyLabel::contextMenuEvent(QContextMenuEvent * ev)
{
QPoint mousePos = ev->pos();
if (m_roiRect.contains(mousePos))
{
m_pOptMenu->exec(QCursor::pos());
}
ev->accept();
}
/**
* @brief 判断鼠标的位置
* @param point 鼠标的位置
*
* @return EmDirection 方向
*/
EmDirection MyLabel::region(const QPoint & point)
{
int mouseX = point.x();
int mouseY = point.y();
QPoint roiTopLeft = m_roiRect.topLeft();
QPoint roiBottomRight = m_roiRect.bottomRight();
EmDirection dir = DIR_NONE;
if (mouseX <= roiTopLeft.x() + CORPADDING && mouseX >= roiTopLeft.x() && mouseY <= roiTopLeft.y() + CORPADDING && mouseY >= roiTopLeft.y())
{
//左上角
this->setCursor(Qt::SizeFDiagCursor);
dir = DIR_LEFTTOP;
}
else if (mouseX >= roiBottomRight.x() - CORPADDING && mouseX < roiBottomRight.x() && mouseY <= roiTopLeft.y() + CORPADDING && mouseY >= roiTopLeft.y())
{
//右上角
this->setCursor(Qt::SizeBDiagCursor);
dir = DIR_RIGHTTOP;
}
else if (mouseX <= roiTopLeft.x() + CORPADDING && mouseX >= roiTopLeft.x() && mouseY >= roiBottomRight.y() - CORPADDING && mouseY <= roiBottomRight.y())
{
//左下角
this->setCursor(Qt::SizeBDiagCursor);
dir = DIR_LEFTBOTTOM;
}
else if (mouseX >= roiBottomRight.x() - CORPADDING && mouseX < roiBottomRight.x() && mouseY >= roiBottomRight.y() - CORPADDING && mouseY <= roiBottomRight.y())
{
//右下角
this->setCursor(Qt::SizeFDiagCursor);
dir = DIR_RIGHTBOTTOM;
}
else if (mouseX >= roiBottomRight.x() - EDGPADDING && mouseX <= roiBottomRight.x() && mouseY >= roiTopLeft.y() && mouseY <= roiBottomRight.y())
{
//右边
this->setCursor(Qt::SizeHorCursor);
dir = DIR_RIGHT;
}
else if (mouseY <= roiTopLeft.y() + EDGPADDING && mouseY >= roiTopLeft.y() && mouseX >= roiTopLeft.x() && mouseX <= roiBottomRight.x())
{
//上边
this->setCursor(Qt::SizeVerCursor);
dir = DIR_TOP;
}
else if (mouseY >= roiBottomRight.y() - EDGPADDING && mouseY <= roiBottomRight.y() && mouseX >= roiTopLeft.x() && mouseX <= roiBottomRight.x())
{
//下边
this->setCursor(Qt::SizeVerCursor);
dir = DIR_BOTTOM;
}
else if (mouseX <= roiTopLeft.x() + EDGPADDING && mouseX >= roiTopLeft.x() && mouseY >= roiTopLeft.y() && mouseY <= roiBottomRight.y())
{
//左边
this->setCursor(Qt::SizeHorCursor);
dir = DIR_LEFT;
}
else if(m_roiRect.contains(point))
{
//中间
dir = DIR_MIDDLE;
}
else
{
dir = DIR_NONE;
}
return dir;
}
/**
* @brief 缩放矩形
* @param mousePoint 鼠标的位置
*
* @return void
*/
void MyLabel::scaleRect(const QPoint & mousePoint)
{
QRect newRect(m_roiRect.topLeft(), m_roiRect.bottomRight());
int width = mousePoint.x() - m_moveStartPoint.x(); //移动的宽度
int height = mousePoint.y() - m_moveStartPoint.y(); //移动的高度
//根据当前的缩放状态来改变矩形的位置大小信息
switch (m_emCurDir)
{
case DIR_LEFT:
newRect.setLeft(mousePoint.x());
break;
case DIR_RIGHT:
newRect.setRight(mousePoint.x());
break;
case DIR_TOP:
newRect.setTop(mousePoint.y());
break;
case DIR_BOTTOM:
newRect.setBottom(mousePoint.y());
break;
case DIR_LEFTTOP:
newRect.setTopLeft(mousePoint);
break;
case DIR_LEFTBOTTOM:
newRect.setBottomLeft(mousePoint);
break;
case DIR_RIGHTTOP:
newRect.setTopRight(mousePoint);
break;
case DIR_RIGHTBOTTOM:
newRect.setBottomRight(mousePoint);
break;
}
if (newRect.width() < MIN_WIDTH || newRect.height() < MIN_HEIGHT)
{
//缩放的大小限制
return;
}
m_roiRect = newRect;
m_moveStartPoint = mousePoint; //更新鼠标的起始位置
}
/**
* @brief 绘制矩形
* @param mousePoint 鼠标的位置
*
* @return void
*/
void MyLabel::paintRect(const QPoint & mousePoint)
{
this->setCursor(Qt::ArrowCursor); //设置鼠标为指针形状
int width = mousePoint.x() - m_paintStartPoint.x(); //移动的宽度
int height = mousePoint.y() - m_paintStartPoint.y(); //移动的高度
if (width < 0 && height < 0)
{
//鼠标向左上角移动
m_roiRect.setX(mousePoint.x());
m_roiRect.setY(mousePoint.y());
}
else if (width < 0)
{
//鼠标向负X位置移动
m_roiRect.setX(mousePoint.x());
m_roiRect.setY(m_paintStartPoint.y());
}
else if (height < 0)
{
//鼠标向负Y位置移动
m_roiRect.setX(m_paintStartPoint.x());
m_roiRect.setY(mousePoint.y());
}
else
{
//正常 向右下移动
m_roiRect.setX(m_paintStartPoint.x());
m_roiRect.setY(m_paintStartPoint.y());
}
//设置矩形大小 绝对值 避免反方向的产生的负值
m_roiRect.setSize(QSize(abs(width), abs(height)));
}
/**
* @brief 绘制矩形
* @param mousePoint 鼠标的位置
*
* @return void
*/
void MyLabel::moveRect(const QPoint & mousePoint)
{
this->setCursor(Qt::ClosedHandCursor);
int width = mousePoint.x() - m_moveStartPoint.x();
int height = mousePoint.y() - m_moveStartPoint.y();
QRect ret;
ret.setX(m_roiRect.x() + width);
ret.setY(m_roiRect.y() + height);
ret.setSize(m_roiRect.size());
m_roiRect = ret;
m_moveStartPoint = mousePoint;
}
3 其他说明
提供示例1 示例2 的源码,可以在以下链接下载资源,本地环境 QT6 , QT5也可以。
文章下载链接: https://download.csdn.net/download/qq_37529913/86810065
解压密码:123456
本文没有提提供示例3源码,因为准备在公司内部分析,所以不放示例3源代码。
在研究玩示例1和示例2之后大概率可以完成类似博主做完的效果。
有其它问题请留言。