关于MFC使用和多线程随笔--对初学者可能有点作用

 

以前对于MFC界面和多线程这块,一直都是看别人编写的界面,以及听别人说多线程的程序,感觉很神奇,自己一直没有动手去实现,这次终于有机会动手尝试

现将在开发中遇到的问题记下来,供以后使用,也希望对其他朋友有一定的作用:

 

程序功能:对图像序列的分析,其中有开始分析、暂停、继续、下一帧、停止和退出等功能

主要存在的问题,由于在点击“开始分析”时,程序要处理1-10000帧,这时该线程一直被该程序占用,无法响应其它的按钮的响应,因此,需要采用多线程的方法实现,其中程序默认线程是主线程,它主要负责各个按钮的响应,而当点击“开始分析”时,该按钮特别耗时,因此,为其创建子线程,由该子线程完全负责对他的处理。

具体的步骤:

 

1. 线程响应的创建

一般线程需要执行的函数,都是全局函数或者static 函数

static UINT ThreadProc_Start_Analysis(LPVOID pParam);  -- 线程具体的实现

CWinThread* m_pThread;

线程的创建和启动

 m_pThread = AfxBeginThread(ThreadProc_Start_Analysis,this, 0,0, 0,0);

 

this,对应的响应函数所在的类,这样在实现时,需要调用

MFCDlg *mfcdlg = (MFCDlg *)pParam ;

 

2. 线程响应的实现中,需要注意的问题

如果是全局函数,则比较好处理,但是如果是类的成员函数的话,则需要使用静态函数,同时,在静态函数中,仅能够使用静态变量和函数,在使用的变量中都需要使用指针MFCDlg,这样才不会报错!

3. 线程的挂起

  //挂起线程
   m_pThread->SuspendThread();

4.线程的恢复

//恢复线程
  m_pThread->ResumeThread();

 

另外,“开始分析”,“暂停”,“继续”,“下一帧”等按钮中,需要合理使用BOOL型变量,这样才能使他们正常工作,例如,“下一帧”的话,就不能使用原来的程序了

因此,而应该根据对应的变量,重新启动某些代码。

 

5. 线程的通信

1)最简单的方法是通过全局变量进行通信,由于本程序都是类的成员函数和变量,因此,本程序全部使用类的公共变量进行通信

2)然而,有时不是变量值的变化,而是更新界面等操作,因此这里可以使用消息的方法  mfcdlg->PostMessage(WM_THREADMSG_UPDATEDATA,0,0) ;

 

BEGIN_MESSAGE_MAP(CStereo_Match_Depth_MFCDlg, CDialog)
 ON_WM_SYSCOMMAND()
 ON_WM_PAINT()
 ON_WM_QUERYDRAGICON()


 ON_MESSAGE(WM_THREADMSG_UPDATEDATA, OnMsgFunc_UpDateData)
 ON_MESSAGE(WM_THREADMSG_PAINT, OnMsgFunc_Paint)

 ON_WM_MOUSEMOVE()     // 对应的映射
 ON_WM_LBUTTONDOWN()


END_MESSAGE_MAP()

 

其中 WM_THREADMSG_UPDATEDATA消息调用函数OnMsgFunc_UpDateData

 

// 消息映射函数
LRESULT MFCDlg::OnMsgFunc_Paint(WPARAM,LPARAM)
{
 UpdateData(FALSE) ;
 OnPaint();   // 重绘对话框
 UpdateWindow();         

 return 1 ;

}

 

6. 鼠标的响应

 

首先需要添加 ON_WM_MOUSEMOVE()     // 对应的映射
 ON_WM_LBUTTONDOWN()
函数映射语句,然后就是添加对应的具体的获取鼠标的操作了

//  ClientToScreen(&point);//将鼠标坐标转换成屏幕坐标
//  CRect rect;//定义一个矩形框,包含左上角和右下角可访问成员
//  GetDlgItem(IDC_STATIC_DEPTH_IMAGE)->GetClientRect(rect);//获取Picture控件的位置信息,存入rect中
//  GetDlgItem(IDC_STATIC_DEPTH_IMAGE)->ClientToScreen(rect);//转换成屏幕坐标


 CRect rect_ctr;  
  (this->GetDlgItem(IDC_STATIC_DEPTH_IMAGE))->GetWindowRect(&rect_ctr);//获取Picture控件相对屏幕左上角的坐标,  
                                                             //存储到rect_ctr中  
 //CRect rect_dlg;   
 //this->GetWindowRect(&rect_dlg);//获取对话框相对屏幕左上角的坐标,存储到rect_dlg中  

 //***************** test ***********************************  
 //Picture控件左上角相对对话框客户区左上角的坐标  
//    ScreenToClient(rect_ctr);//这里的客户区不包括对话框的状态栏  
//    point.x =  rect_ctr.left;//  
//    point.y =  rect_ctr.top;  

 //***************** test ***********************************  
 //对话框窗口左上角相对对话框客户区左上角的坐标  
 //  ScreenToClient(rect_dlg);//这里的客户区不包括对话框的状态栏  
 //  point.x = rect_dlg.left;  
 //  point.y = rect_dlg.top;  

     this->ScreenToClient(rect_ctr);//获取Picture控件相对对话框客户区左上角的坐标   
  
 point.x -=  rect_ctr.left;//point获取的是鼠标相对对话框客户区左上角的坐标,减去rect_ctr.left和  
 point.y -=  rect_ctr.top;//rect_ctr.top后,即为鼠标相对Picture控件左上角的坐标

 int rw = rect_ctr.BottomRight().x - rect_ctr.TopLeft().x;   // 求出图片控件的宽和高
 int rh = rect_ctr.BottomRight().y - rect_ctr.TopLeft().y ; //  rect_ctr.bottom - rect_ctr.top;

 if (rw > Depth_Image->width)
 {
  point.x = point.x - ((rw - Depth_Image->width)>>1) ;
 }
 else
 {
  point.x = point.x + ((Depth_Image->width -rw)>>1) ;
 }

 if (rh > Depth_Image->height)
 {
  point.y = point.y - ((rh-Depth_Image->height)>>1) ;
 }
 else
 {
  point.y = point.y + ((Depth_Image->height-rh)>>1) ;
 }

 if (point.x< 0 || point.y < 0 || (point.x > Depth_Image->width) || ((point.y) > Depth_Image->height))
 {
  m_nCurrent_X_Coordinate = -1 ;
  m_nCurrent_Y_Coordinate = -1 ;
 }
 else
 {
  m_nCurrent_X_Coordinate = point.x ;
  m_nCurrent_Y_Coordinate = point.y ;
 }

 if (m_nCurrent_X_Coordinate < 0 || m_nCurrent_Y_Coordinate < 0 )
 {
  m_dCurrent_Point_Depth = 0 ;
 }
 else
    m_dCurrent_Point_Depth = stereo_depth_info.DepthInfo->data.fl[m_nCurrent_Y_Coordinate*Depth_Image->width+m_nCurrent_X_Coordinate];    //Depth_Image->imageData[m_nCurrent_Y_Coordinate*Depth_Image->width+m_nCurrent_X_Coordinate] ;


 if (m_nCurrent_X_Coordinate >= 0 || m_nCurrent_Y_Coordinate >=0 || m_nCurrent_X_Coordinate<=Depth_Image->width || m_nCurrent_Y_Coordinate<=Depth_Image->height)
 {
  UpdateData(FALSE) ;
 }
 
 CDialog::OnMouseMove(nFlags, point);

 

以上是为了获得以图像左上角为原点的图像坐标

 

7. 线程的释放

 DWORD dwCode;
 GetExitCodeThread(m_pThread->m_hThread, &dwCode); 

 if (dwCode == STILL_ACTIVE)
 {
  printf("The Thread is still running!\n");
 }
 TerminateThread(m_pThread->m_hThread, dwCode);

 CloseHandle(m_pThread->m_hThread);

 

 

以上能工作,但不是很安全,可能程序的有些资源没有释放完全

 

8. UpdateData的使用

该函数对于UpdateData非常有用,需要用其对界面进行更新和从界面获得对应的值

// UpdateData(true)从界面将值传到变量,UpdateData(false)将值从变量传到界面

 

9. 多字节问题

有时 LPCSTR 和 const char []之间会出现转换问题,例如mfcdlg->MessageBox( "已完成要求的计算!");在单字节的设置下就有问题,但是多字节就没有问题

 

10. 对于Radio控件

注意设置group按钮的属性,是TRUE还是FALSE,如果相邻的控件,只有一个Group属性,则他们归属于一组,否则他们就是两个独立的控件

 

11. 变量的初始化和内存的分配

可以将其全部放在OnInitDialog,在变量的初始化时,不要忘了设置UPDATEDATA(FALSE),这样才能更新界面

 

12. 图像的显示

在OnPaint函数中添加

CDialog::OnPaint();   // 重绘对话框
  CDialog::UpdateWindow();                // 更新windows窗口,如果无这步调用,图片显示还会出现问题
  Show_ORI_Image(Ori_Left_Image,IDC_STATIC_LEFT_ORI_IMAGE) ; // IDC_STATIC_LEFT_ORI_IMAGE对应的Picture控件的ID,需要显示的图片Ori_Left_Image

 

void MFCDlg::Show_ORI_Image(IplImage* img, UINT ID)
{
 CDC* pDC = GetDlgItem( ID ) ->GetDC();  // 获得显示控件的 DC
 HDC hDC = pDC ->GetSafeHdc();    // 获取 HDC(设备句柄) 来进行绘图操作

 CRect rect;
 GetDlgItem(ID) ->GetClientRect( &rect );
 int rw = rect.right - rect.left;   // 求出图片控件的宽和高
 int rh = rect.bottom - rect.top;
 int iw = img->width;      // 读取图片的宽和高
 int ih = img->height;
 int tx = (int)(rw - iw)/2;     // 使图片的显示位置正好在控件的正中
 int ty = (int)(rh - ih)/2;
 SetRect( rect, tx, ty, tx+iw, ty+ih );

 CvvImage cimg;
 cimg.CopyOf( img );       // 复制图片
 cimg.DrawToHDC( hDC, &rect );    // 将图片绘制到显示控件的指定区域内

 ReleaseDC( pDC );
}

 

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值