基于dcmtk的dicom工具 第十二章 响应鼠标消息实现图像的调窗、缩放、移动

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录


前言

前两章成功加载dicom文件,并显示到对话框中。本章介绍用户通过鼠标操作,实现调窗、缩放、移动三个功能。

  1. 鼠标左键按下+鼠标移动->调窗
  2. 鼠标中键按下+鼠标移动->移动
  3. 鼠标右键按下+鼠标移动->缩放

效果如下:


一、添加鼠标消息

1. 类向导添加Displayer鼠标消息

在vs2017中,点击菜单"项目"->"类向导..." 或才按快捷键 Ctrl+Shift+X 调出"类向导"对话框,类名选择Displayer,在"消息"选项卡中添加 WM_LBUTTONDOWN,WM_LBUTTONUP,WM_MBUTTONDOWN,WM_MBUTTONUP,WM_RBUTTONDOWN,WM_RBUTTONUP,

WM_MOUSEMOVE七个消息。

2. 响应代码

OnLButtonDown, OnRButtonDown, OnMButtonDown中记录鼠标按下位置和鼠标操作开始

OnLButtonUp,OnRButtonUp,OnMButtonUp中记录鼠标操作结束

OnMouseMove中根据前面记录的鼠标操作类型分别调用AdjustWindow进行调窗,Pan进行移动,Scale进行缩放

头文件内联函数:

cpp 复制代码
...
private:
	enum MouseAction {
		MA_None,
		MA_Window,   // 调窗
		MA_Pan,      // 移动
		MA_Scale     // 缩放
	};

	MouseAction m_action;
	CPoint m_lastPoint;

	void startWindow() {
		m_action = MA_Window;
	}
	void endWindow() {
		m_action = MA_None;
	}
	void startPan() {
		m_action = MA_Pan;
	}
	void endPan() {
		m_action = MA_None;
	}
	void startScale() {
		m_action = MA_Scale;
	}
	void endScale() {
		m_action = MA_None;
	}

源文件:

cpp 复制代码
void Displayer::OnLButtonDown(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	startWindow();
	m_lastPoint = point;
	m_hCursor = m_curWin;
	SetCursor(m_hCursor);

	CWnd::OnLButtonDown(nFlags, point);
}


void Displayer::OnLButtonUp(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	endWindow();
	m_hCursor = m_curArrow;
	SetCursor(m_hCursor);

	CWnd::OnLButtonUp(nFlags, point);
}


void Displayer::OnRButtonDown(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	startScale();
	m_lastPoint = point;
	m_hCursor = m_curScale;
	SetCursor(m_hCursor);

	CWnd::OnRButtonDown(nFlags, point);
}


void Displayer::OnRButtonUp(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	endScale();
	m_hCursor = m_curArrow;
	SetCursor(m_hCursor);

	CWnd::OnRButtonUp(nFlags, point);
}


void Displayer::OnMButtonDown(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	startPan();
	m_lastPoint = point;
	m_hCursor = m_curPan;
	SetCursor(m_hCursor);

	CWnd::OnMButtonDown(nFlags, point);
}


void Displayer::OnMButtonUp(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	endPan();
	m_hCursor = m_curArrow;
	SetCursor(m_hCursor);

	CWnd::OnMButtonUp(nFlags, point);
}


void Displayer::OnMouseMove(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	int deltaX = point.x - m_lastPoint.x;
	int deltaY = point.y - m_lastPoint.y;

	CRect rc;
	GetClientRect(&rc);

	switch (m_action)
	{
	case Displayer::MA_None:
		break;
	case Displayer::MA_Window:
		AdjustWindow(deltaX, deltaY);
		break;
	case Displayer::MA_Pan:
		Pan(deltaX, deltaY);
		break;
	case Displayer::MA_Scale:
		Scale(deltaY);
		break;
	default:
		break;
	}

	m_lastPoint = point;
	CWnd::OnMouseMove(nFlags, point);
}

二、调窗

AdjustWindow函数,鼠标上下移动改变窗位,鼠标左右移动改变窗宽。根据dicom图像最大最小像素值自动计算调窗幅度。

AdjustWindow需要调用CreateDIB生成新的位图数据。

Displayer类:

cpp 复制代码
void Displayer::AdjustWindow(int deltaX, int deltaY)
{
	// 根据图像最大最小像素值计算调窗值,窗宽不小于1
	double minVal, maxVal;
	m_pImage->GetRange(minVal, maxVal);

	double deltaWW = (maxVal - minVal) / 1024 * deltaX;
	double deltaWC = (maxVal - minVal) / 1024 * deltaY;
	m_drawParam.winWidth += deltaWW;
	m_drawParam.winCenter += deltaWC;
	if (m_drawParam.winWidth < 1) m_drawParam.winWidth = 1;

	m_pImage->CreateDIB(m_pDib, m_drawParam.width, m_drawParam.height, m_drawParam.winCenter,
		m_drawParam.winWidth, m_drawParam.nFrame, m_drawParam.bNagtive, true);
	Invalidate();
}

DcmParser类:

cpp 复制代码
void DcmParser::GetRange(double& minVal, double& maxVal)
{
	if (m_pDcmImg) {
		m_pDcmImg->getMinMaxValues(minVal, maxVal);
	}
}

CBaseImage类,CDicomImage类

cpp 复制代码
class CBaseImage
{
public:
	CBaseImage();
	virtual ~CBaseImage();

public:
	PatientInfo& GetPatientInfo();
	StudyInfo&   GetStudyInfo();
	SeriesInfo&  GetSeriesInfo();
	ImageInfo&   GetImageInfo();
	....
	// 增加接口函数
	virtual void GetRange(double& minVal, double& maxVal) = 0;
}

void CDicomImage::GetRange(double& minVal, double& maxVal)
{
	m_parser.GetRange(minVal, maxVal);
}

三、移动

Pan函数移动图像,只需要修改绘制参数nOffsetX,nOffsetY,调用Invalidate重绘即可。

cpp 复制代码
void Displayer::Pan(int deltaX, int deltaY)
{
	m_drawParam.nOffsetX += deltaX;
	m_drawParam.nOffsetY += deltaY;
	Invalidate();
}

四、缩放

Scale函数缩放图像,只需要修改绘制参数zoom,调用Invalidate重绘即可。

cpp 复制代码
void Displayer::Scale(int delta)
{
	bool reDraw = true;
	float factor = m_drawParam.zoom*0.02;
	if (delta > 0) {
		if ((m_drawParam.zoom - factor)*m_drawParam.width < 20
			|| (m_drawParam.zoom - factor)*m_drawParam.height < 20)
		{
			reDraw = false;
		}
		else {
			m_drawParam.zoom -= factor;
		}
	}
	else {
		if (m_drawParam.zoom + factor > 10) {
			reDraw = false;
		}
		else {
			m_drawParam.zoom += factor;
		}
	}

	if (reDraw)
		Invalidate();
}

五、自定义光标

当不同鼠标按键按下时,会切换不同的光标以表示当前正使用的功能,在MFC中使用自定义光标参考文章MFC 使用自定义光标

相关推荐
不会代码的小猴15 小时前
21. 泛型编程上
开发语言·c++·笔记·算法
青瓦梦滋16 小时前
传输层UDP/TCP协议
linux·网络·c++·网络协议·tcp/ip·udp
一只旭宝16 小时前
细讲C加加【9】C++ std::function与std::bind详解|仿函数、绑定器、类成员绑定、占位符、成员偏移指针
开发语言·c++·算法
Lhan.zzZ18 小时前
在 Visual Studio 2022 中打造可扩展的动态链接库模块:从零搭建到原理解析
开发语言·c++·visual studio
zh路西法20 小时前
【3D SLAM源码解读系列】(二)Small_gicp——5 个积木搭出最优点云配准
c++·pcl·icp·fastgicp·smallgicp·gicp
fpcc1 天前
ubuntu26环境下的开发环境安装处理
c++·并行编程
charlie1145141911 天前
Cinux · 第一次跳进 Ring 3:用户态与特权隔离
开发语言·c++·操作系统·开源项目
别动我齐刘海1 天前
机器学习基础2——C++、OpenCV、点云、Open3D
c++·人工智能·opencv·机器学习·计算机视觉·机器人·ros2
躺不平的理查德1 天前
Windows C++ 第三方库使用流程备忘录--OpenCV
开发语言·c++
余额瞒着我当琳1 天前
C++STL容器string--迭代器,string的接口,string的遍历,访问方式
c++