提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
前两章成功加载dicom文件,并显示到对话框中。本章介绍用户通过鼠标操作,实现调窗、缩放、移动三个功能。
- 鼠标左键按下+鼠标移动->调窗
- 鼠标中键按下+鼠标移动->移动
- 鼠标右键按下+鼠标移动->缩放
效果如下:

一、添加鼠标消息
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进行缩放
头文件内联函数:
...
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;
}
源文件:
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类:
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类:
void DcmParser::GetRange(double& minVal, double& maxVal)
{
if (m_pDcmImg) {
m_pDcmImg->getMinMaxValues(minVal, maxVal);
}
}
CBaseImage类,CDicomImage类
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重绘即可。
void Displayer::Pan(int deltaX, int deltaY)
{
m_drawParam.nOffsetX += deltaX;
m_drawParam.nOffsetY += deltaY;
Invalidate();
}
四、缩放
Scale函数缩放图像,只需要修改绘制参数zoom,调用Invalidate重绘即可。
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 使用自定义光标

被折叠的 条评论
为什么被折叠?



