赞
踩
1.消息触发,用户从界面获取响应消息;
2.鼠标创建初始的矩形ROI;
3.支持多ROI;
4.支持选择,拉伸,移动和删除;
5.开发环境,MFC 对话框程序
要想实现图形的拉伸功能,可以借用vs函数库中封装的橡皮筋类(CRectTracker),达到事半功倍的效果。
首先,简要介绍一下CRectTracker这个类:
Windows自带的画图软件中可以用虚线框选择图像的某个区域,之后便可以拖动、放大、缩小该区域,这是通过橡皮筋类(CRectTracker)来实现的,它将实现用线框选中一个区域,并可以拖动、放大、缩小该区域。
CRectTracker类允许一个项被显示,移动,以不同的方式改变大小。虽然CRectTracker类是设计来支持用户以图形化界面与OLE项交互的,但是它的使用不仅限于支持OLE的应用程序。它可以使用在任何需要用户界面的地方。
CRectTracker的边框可以是实线,也可以是点线。可给予项一种阴影式边框或用一种阴影样式覆盖项,用来指示项的不同状态。你可以在项的外界或内部放置八个调整大小把手。(有关八个调整大小把手的解释,参见GetHandleMask。)最后,一个CRectTracker允许你在调整项的大小时改变项的方向。
要使用CRectTracker,首先要构造一个CRectTracker对象,并指定用哪种显示状态来初始化。然后,应用程序就可以使用这个界面,提供给用户有关与CRectTracker对象相关联的OLE项当前状态的直观反馈了。
#include <afxext.h>
请参阅:
COleResizeBar, CRect, CRectTracker::GetHandleMask
CRectTracker类成员
数据成员
m_nHandleSize | 确定调整大小把手的尺寸 |
m_rect | 矩形的以像素表示的当前位置 |
m_sizeMin | 确定矩形宽度和高度的最小值 |
m_nStyle | 跟踪器的当前风格 |
构造
构造一个CRectTracker对象 |
操作
Draw | 显示矩形,并显示八个调整把手(调用时若要正常显示调整把手,不能只是在重绘函数中调用,还需要在程序当前位置调用一次) |
GetTrueRect | 返回矩形的宽度和高度,包括改变大小句柄 |
HitTest | 返回与CRectTracker对象关联的光标的当前位置(返回值>=0:在矩形内,小于0:矩形外) |
NormalizeHit | 规范化一个单击测试代码 |
SetCursor | 根据光标在矩形上方的位置来设置光标 |
Track | 支持用户操作矩形(会捕捉鼠标左键弹起消息,一直到鼠标左键弹起才会执行其下面的程序;双击鼠标左键,将会当做响应Track完毕,并执行一次左键按下一次,左键弹起一起) |
TrackRubberBand | 支持用户“橡皮筋”似的拉伸选择(此函数主要用于拉取矩形,用于选择区域内的矩形区域) |
可重载
AdjustRect | 当矩形被改变大小时此函数被调用 |
DrawTrackerRect | 当画一个CRectTracker对象的边框时此函数被调用 |
OnChangedRect | 当矩形被改变大小或被移动时,此函数被调用 |
GetHandleMask | 调用此函数来获得一个CRectTracker项的调整大小把手的掩码 |
☆需要注意的是:CRectTracker类并不是用来画矩形区域的,而是用来选择区域的!!!如果要实现画区域边框,还得利用其它绘制函数~
下面是实现的源代码:
首先,在stdafx.h头文件中加入
#define MAX_RECT_NUM 100 //允许画的最多的矩形个数
在头文件中添加相关的变量和函数声明,
- //用于创建ROI相关的 S
-
- CRectTracker m_rctCurTracker; //当前选中的矩形区域
-
- CRectTracker m_rctTracker[MAX_RECT_NUM]; //用于存储已画的矩形区域
-
- bool m_IsChose; //标记是否被选中
-
- bool m_IsDraw; //标记“绘制”按钮是否按下
-
- int m_rectNum; //当前实际已经画的矩形的个数
-
- int m_rctChoseNum;//当前选中的矩形的编号
-
- int m_FlaMoveStep;//键盘方向键每响应一次的图像移动的像素单位上的步长
-
- int dirct; //用于标记那个方向键按下。1:左,2:右,3:上,4:下,5:delete(删除)
-
-
-
- //鼠标在ROI区域时光标变换
-
- afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message);
-
- //绘制ROI 触发按键
-
- afx_msg void OnBnClickedBtnDrawroi();
-
- // 键盘消息时的移动方向
-
- virtual BOOL PreTranslateMessage(MSG* pMsg);
-
- //移动+删除操作,改变区域
-
- void ChangeRectPt(int ChangeDirct);
-
- 用于创建ROI相关的 E

//键盘消息时的移动方向
ROI相关变量在InitDialog对话框中初始化
- BOOL CXXXDlg::OnInitDialog()
-
- {
-
- CDialogEx::OnInitDialog();
-
- // 将“关于...”菜单项添加到系统菜单中 // IDM_ABOUTBOX 必须在系统命令范围内。
-
- ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
-
- ASSERT(IDM_ABOUTBOX < 0xF000);
-
- CMenu* pSysMenu = GetSystemMenu(FALSE);
-
- if (pSysMenu != nullptr)
-
- {
-
- BOOL bNameValid;
-
- CString strAboutMenu;
-
- bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
-
- ASSERT(bNameValid);
-
- if (!strAboutMenu.IsEmpty())
-
- {
-
- pSysMenu->AppendMenu(MF_SEPARATOR);
-
- pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
-
- }
-
- }
-
- // 设置此对话框的图标。 当应用程序主窗口不是对话框时,框架将自动 // 执行此操作
-
- SetIcon(m_hIcon, TRUE); // 设置大图标
-
- SetIcon(m_hIcon, FALSE); // 设置小图标
-
- // TODO: 在此添加额外的初始化代码
-
- /ROI相关的初始化 S
-
- m_rctCurTracker.m_rect.SetRect(0, 0, 0, 0);//设置矩形区域大小
-
- m_rctCurTracker.m_nStyle = CRectTracker::dottedLine | CRectTracker::resizeInside;
-
- m_rctCurTracker.m_nHandleSize = 6;
-
- for (int i = 0; i < MAX_RECT_NUM; i++)
-
- {
-
- m_rctTracker[i].m_rect.SetRect(0, 0, 0, 0);//设置矩形区域大小
-
- m_rctTracker[i].m_nStyle = CRectTracker::dottedLine | CRectTracker::resizeInside;
-
- m_rctTracker[i].m_nHandleSize = 6;
-
- }
-
-
- m_IsChose = FALSE;//表示未选中
-
- m_IsDraw = false;
-
- m_rectNum = 0;
-
- m_rctChoseNum = 0;
-
- m_FlaMoveStep = 2;
-
- dirct = 0;
-
- /ROI相关的初始化 E
-
- return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
-
- }

在Paint函数中绘制当前ROI
- void CXXXDlg::OnPaint()
-
- {
-
- CPaintDC dc(this); // 用于绘制的设备上下文
-
- if (IsIconic())
-
- {
-
- SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
-
- // 使图标在工作区矩形中居中
-
- int cxIcon = GetSystemMetrics(SM_CXICON);
-
- int cyIcon = GetSystemMetrics(SM_CYICON);
-
- CRect rect;
-
- GetClientRect(&rect);
-
- int x = (rect.Width() - cxIcon + 1) / 2;
-
- int y = (rect.Height() - cyIcon + 1) / 2;
-
-
-
- // 绘制图标
-
- dc.DrawIcon(x, y, m_hIcon);
-
- }
-
- else
-
- {
-
- if (m_IsChose)
-
- {
-
- //若选择了该区域,则显示边框以及8个调整点
-
- m_rctCurTracker.Draw(&dc);
-
- //输出坐标信息
-
- CRect rect = m_rctCurTracker.m_rect;
-
- CString strLT = _T("");
-
- strLT.Format(_T("%d,%d"),rect.left,rect.top);
-
- SetDlgItemTextA(IDC_EDIT_CURROILT, strLT);
-
- CString strRB = _T("");
-
- strRB.Format(_T("%d,%d"), rect.right, rect.bottom);
-
- SetDlgItemTextA(IDC_EDIT_CURROIRB, strRB);
-
-
-
- //输出当前ROI的长宽信息
-
- CString strWidth = _T("");
-
- strWidth.Format(_T("%d"), rect.Width());
-
- SetDlgItemTextA(IDC_EDIT_ROIWIDTH, strWidth);
-
-
-
- CString strHeight = _T("");
-
- strHeight.Format(_T("%d"), rect.Height());
-
- SetDlgItemTextA(IDC_EDIT_ROIHEIGHT, strHeight);
-
- }
-
-
-
- CPen pen(PS_SOLID, 1, RGB(100, 255, 200));
-
- dc.SelectObject(&pen);
-
-
-
- CBrush *pbrush = CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
-
- dc.SelectObject(pbrush);
-
- CRect rect;
-
- m_rctCurTracker.GetTrueRect(&rect);//得到矩形区域的大小
-
- dc.Rectangle(&rect);//画出矩形
-
- CSize rct_size;
-
- for (int i = 0; i < MAX_RECT_NUM; i++)
-
- {
-
- m_rctTracker[i].GetTrueRect(&rect);//得到矩形区域的大小
-
- rct_size = m_rctTracker[i].m_rect.Size();
-
-
-
- if (rct_size.cx * rct_size.cy == 0 || i == m_rctChoseNum)
-
- {
-
- continue;
-
- }
-
- dc.Rectangle(&rect);//画出矩形
-
- }
-
- //CRect rect1;
-
- /*rect1.top=rect.top+3;
- rect1.bottom=rect.bottom-3;
- rect1.left=rect.left+3;
- rect1.right=rect.right-3;
- pbrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
- dc.SelectObject(&brush);
- dc.Rectangle(&rect1);*/
-
-
-
- CDialogEx::OnPaint();
-
- }
-
- }

鼠标左键按下后,开始绘制ROI
- void CXXXDlg::OnLButtonDown(UINT nFlags, CPoint point)
-
- {
-
- // TODO:在此添加消息处理程序代码和/或调用默认值
-
- bool IsInRct = false;
-
- int i = 0;
-
- do
-
- {
-
- if (m_rctTracker[i].HitTest(point) < 0)
-
- {
-
- IsInRct = false;
-
- }
-
- else
-
- {
-
- IsInRct = true;
-
- m_rctChoseNum = i;
-
- m_rctCurTracker = m_rctTracker[m_rctChoseNum];
-
- m_IsChose = true;
-
- break;
-
- }
-
- i++;
-
- } while (i < m_rectNum);
-
-
-
- if (!IsInRct)
-
- {
-
- CRectTracker tempRectTracker;
-
- CRect rect;
-
- tempRectTracker.TrackRubberBand(this, point);
-
- tempRectTracker.m_rect.NormalizeRect();
-
- if (rect.IntersectRect(tempRectTracker.m_rect, m_rctCurTracker.m_rect))
-
- m_IsChose = TRUE;
-
- else
-
- {
-
- m_IsChose = false;
-
- if (m_IsDraw)
-
- {
-
- //m_IsChose=FALSE;
-
- m_rctTracker[m_rectNum].m_rect = tempRectTracker.m_rect;
-
- m_rctCurTracker.m_rect = m_rctTracker[m_rectNum].m_rect;
-
- CClientDC dc(this);
-
- m_rctCurTracker.Draw(&dc);
-
- //注意!!在这里一定要调用绘制边框的程序,否则单凭onpaint中绘制,不能显示出来
-
- m_rctChoseNum = m_rectNum;
-
- m_rectNum++;
-
- if (m_rectNum >= MAX_RECT_NUM)
-
- {
-
- m_rectNum = MAX_RECT_NUM;
-
- MessageBoxA("已画矩形超过上限,不能再画矩形区域", "警告", MB_OK);
-
- }
-
- m_IsChose = TRUE;
-
- m_IsDraw = false;
-
-
-
- Invalidate();
-
- }
-
- }
-
- Invalidate();
-
- }
-
- else
-
- {
-
- CClientDC dc(this);
-
- m_rctCurTracker.Draw(&dc);
-
- m_rctCurTracker.Track(this, point);
-
- m_rctCurTracker.m_rect.NormalizeRect();
-
- m_rctTracker[m_rctChoseNum] = m_rctCurTracker;
-
- m_IsChose = TRUE;
-
- Invalidate();
-
- }
-
-
-
- CDialogEx::OnLButtonDown(nFlags, point);
-
- }

绘制ROI指令入口
- void CXXXDlg::OnBnClickedBtnDrawroi()
-
- {
-
- // TODO: 在此添加控件通知处理程序代码
-
- m_IsDraw = true;
-
- }
鼠标在ROI区域时,光标变换
- BOOL CXXXDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
-
- {
-
- //TODO: 在此添加消息处理程序代码和/或调用默认值
-
- if (pWnd == this && m_rctCurTracker.SetCursor(this, nHitTest))
-
- {
-
- return TRUE;
-
- }
-
- return CDialogEx::OnSetCursor(pWnd, nHitTest, message);
-
- }

ROI 移动删除 通过键盘来实现
- BOOL CAOITestDemoDlg::PreTranslateMessage(MSG* pMsg)
-
- {
-
- // TODO: 在此添加专用代码和/或调用基类
-
- if (pMsg->message == WM_KEYDOWN)
-
- {
-
- switch (pMsg->wParam)
-
- {
-
- case VK_LEFT:
-
- dirct = 1;
-
- break;
-
- case VK_RIGHT:
-
- dirct = 2;
-
- break;
-
- case VK_UP:
-
- dirct = 3;
-
- break;
-
- case VK_DOWN:
-
- dirct = 4;
-
- break;
-
- case VK_DELETE:
-
- dirct = 5;
-
- break;
-
- default:
-
- dirct = 0;
-
- }
-
- }
-
- ChangeRectPt(dirct);
-
- return CDialogEx::PreTranslateMessage(pMsg);
-
- }

ROI区域改变后需重新绘制,函数定义如下,
- void CXXXDlg::ChangeRectPt(int ChangeDirct)
-
- {
-
- CRect rct;
-
- rct = m_rctCurTracker.m_rect;
-
- switch (ChangeDirct)
-
- {
-
- case 1://左移
-
- rct.TopLeft().x -= m_FlaMoveStep;
-
- rct.BottomRight().x -= m_FlaMoveStep;
-
- break;
-
- case 2://右移
-
- rct.TopLeft().x += m_FlaMoveStep;
-
- rct.BottomRight().x += m_FlaMoveStep;
-
- break;
-
- case 3://下移
-
- rct.TopLeft().y -= m_FlaMoveStep;
-
- rct.BottomRight().y -= m_FlaMoveStep;
-
- break;
-
- case 4://上移
-
- rct.TopLeft().y += m_FlaMoveStep;
-
- rct.BottomRight().y += m_FlaMoveStep;
-
- break;
-
- case 5://删除
-
- m_rctCurTracker.m_rect.SetRect(0, 0, 0, 0);
-
- m_rctTracker[m_rctChoseNum] = m_rctCurTracker;
-
- dirct = 0;
-
- Invalidate();
-
- return;
-
- }
-
- m_rctCurTracker.m_rect.SetRect(rct.TopLeft(), rct.BottomRight());
-
- m_rctTracker[m_rctChoseNum] = m_rctCurTracker;
-
- if (ChangeDirct != 0)
-
- {
-
- Invalidate();
-
- }
-
- dirct = 0;
-
- }
-

以上功能添加完成后,效果如下,
到这一步,ROI绘制相关的需求已完成。这里ROI不是绘制在Picture控件上,而是整个窗体,离我们项目中的需求还有一段距离。待继续实现:
1.ROI的坐标改为相对Picture控件左上顶点为原点;
2.在Picture控件上绘制,移动ROI。
待续。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。