MFC多线程的应用及更新主界面的图片显示视频2020-08-03 21:05:54

( 1人已投票,[高质量] )
分享:
31.3K

MFC不用多说,是封装了基础win API,让开发效率真的是事半攻倍。由于各种封装,N多种的类型都出来了,本章节只介绍多线程的基础应用的,MFC对于初学者来说真是晦涩难懂的,我写文章也很讨厌内容太长,尽量言简意赅了,但是这里面有少事,如果你是初学者,看过之后绝对让你有所收获!因为我不会时就渴望这样的文章。。。

多线程的必要性:由于主界面UI不可被阻塞,比如用一个while(1)来重复做一件事的话,在做事过程中,主界面会分取不到cpu片时间来处理其他事务,造成阻塞,比如,界面的按纽无法点击,窗口无法拖动等一切操作,也或是一个耗时的操作,虽然没有while循环,由于处理时间长,都会造成主界面无法操作,这就是常说的阻塞。多线程的出现就是为此情况而生,下面来说一下,在MFC下,实现多线程异步处理事务根据需求主要是“三大法宝”!对于初学者来说,这至关重要!本章还会简单介绍在线程内引用外部变量,及操作主界面的UI。关于线程的更多操作,比如挂起、终止、彼此通信、及广播message后续会更新内容,敬请期待。。。


实现多线程也要根据需求:一般分为三类

一、经过MFC封装的 AfxBeginThread() 也叫工作者线程

二、原生的 CreateThread()上面那种最终也是调用它,学会了,以后在console应用下也一样操作,但是没有上面简单

三、_beginthread() 主要用于C语言使用


这里主要讲解用AfxBeginThread来实现多线程操作。想要快速的用上多线程,皮皮虾,我们走!MFC多线程的应用及更新主界面的图片显示视频

看下原生的CretateThread函数原型

HANDLE CreateThread(
              NULL, // 没有安全描述符
              0, // 默认线程栈的大小
              MyThreadProc, // 线程函数指针,即函数名
              (LPVOID)&n, // 传递参数
              NULL, // 没有附加属性
              NULL // 不需要获得线程号码
              );


应用:

1)在需要的地方添加,比如点击按纽:

AfxBeginThread(HandleTestProceed, (LPVOID)this);

HandleTestProceed是我自己的多线程函数,等下就创建它,后面的this很重要哦,LPVOID就是个万能通用类型,看吧,this主界面的句柄被传走了,以后通信就靠TA了。

2)创建接收函数HandleTestProceed,名字是我自己编的,里面有Test新手不要觉得是系统函数。。。

在.h文件中添加头声明://static或全局类型是多线程专有要求!!!

static UINT HandleTestProceed(LPVOID lParam);

在.cpp文件中添加实现(我的class中有Test,换成你的)

UINT CTestClass::HandleTestProceed(LPVOID lParam)
{
  CTestClass* pWnd = (CTestClass*)lParam; //线程内掉用线程外的内容的方式
  //pWnd->MyTestFun();     //要执行的函数
  //pWnd->myTestParmter123;   //要调用的变量
  bVdoStart = true;//这个是用于终止线程一个bool变量,随便在外部顶上声明下
  while (true) {
    if (!bVdoStart) break;     
      //mySecondFun(pWnd->pWnd1);
      Sleep(200);
    }
  }
  return 1;
}

讲解:经过这两步,就实现了多线程,我写的这是个最基础模板,这里包含着巨量信息!里包包含while用于循环操作,包含了条件终止的flag、包含了操作主界面的句柄!

代码中的注释第一个与第二个为可以在多线程内调用主线程中的函数,记住,static要调用static,要实现这个函数也需要static一致,不过如果没注意,编译时候会提示你的。注释掉的第三处内容为while循环里的单步操作,这样分开逻辑比较清晰,如果你是简单测试,可以去掉while直接实现你的内容。

说了后面再讲线程的中止的,但是既然到这儿了,就提一句吧,bVdoStart这个可以搞成公共变量static,比如如果程序退出了,可以调用对其进行终止:

bVdoStart = false;
::PostMessage(AfxGetMainWnd()->m_hWnd, WM_CLOSE, 0, 0);//退出程序

讲解,不用再查了,这也是最常用的退出MFC程序的四大方法之一


MFC你一定逃不过更新主界面,下面稍微讲解一下在线程内更新主界面,一般两种方法,通过句柄直接更新,另一种是线程内只做数据处理,然后发送message让主程序更新,我也查了下,看到不少人在这块出问题,总是崩溃,第二种的方法人以后我会更新经验心得,现在只在这里说下如何能安全的用句柄在线程内更新主界面,这里是更新Picture label,需要你预先在窗口上拖放一个图片标签,设定ID为IDC_PICTURE,更新标签内容主要分三步:

1)绑定图片控件

2)初始化相关参数

3)把图片画上去 - StretchDIBits


上面也提到了,在线程内调用外部变量很容易,那就把图片控件放在外面,你把他弄成成员变量最好,在调用前确保被初始化就好,一般放在OnInitDialog下就行,绑定也容易,只需一句话:

CWnd* pWnd1 = GetDlgItem(IDC_PICTURE);

初始化参数有点麻烦,按理来说,直接把图片显示出来应该很容易,但是很麻烦,很有MFC特色,仔细分析也有道理,因为需要知道像素、大小、通道等内容,具本查看StretchDIBits函数原型,因为我用的是opencv得到的图片是mat类型的,如果你不是,问题也不大,只要你的bitmap对象能获取参数就行,试一下不行就转换,没啥难度,这里主要介绍操作调用流程,使你学会东西。

首先来搞个BITMAPINFO参数

//Mat matSrc = ... //下面需一个图片的对象
 static bool First = TRUE;
 if (First)
 {
   BYTE* bitBuffer = new BYTE[40 + 4 * 256];//开辟一个内存区域
   if (bitBuffer == NULL)
   {
     return 1;
   }
   First = FALSE;
   memset(bitBuffer, 0, 40 + 4 * 256);
   bitMapinfo = (BITMAPINFO*)bitBuffer;
   bitMapinfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
   bitMapinfo->bmiHeader.biPlanes = 1;
   for (int i = 0; i < 256; i++)
   { //颜色的取值范围 (0-255)
     bitMapinfo->bmiColors[i].rgbBlue = bitMapinfo->bmiColors[i].rgbGreen = bitMapinfo->bmiColors[i].rgbRed = (BYTE)i;
   }
 }
 bitMapinfo->bmiHeader.biHeight = -matSrc.rows;
 bitMapinfo->bmiHeader.biWidth = matSrc.cols;
 bitMapinfo->bmiHeader.biBitCount = matSrc.channels() * 8;


这代码码来自网上,仔细看一下这个First好像有点Bug,意思却是很明确,但下次调用的时候还是分配一个小空间的,意义不大,最完美的办法就是把这行放到while之外,或是把First拿出去搞成公共变量,当前无关痛痒,到这里bitMapinfo就初始化好了,如果bitMapinfo听我的放在while外面了,就把他当参数传给需要地方。不用每次都初始化检查它。

还有两个小变量,在显示前初始化就行,hdc是设备上下句柄,与CDC、DC是常用的变量,三个也是可以互相转换的,这里不用知道,CRect crect(a,b,c,d); a,b代表的是矩形区域的左上角的x和y坐标,c,d代表的是矩形区域的右下角的x和y坐标,这样就圈出来了一个矩形,代码:

//Mat matSrc = ... //你的图片对象
CRect drect;
  pWnd->GetClientRect(drect);  //pWnd指向CWnd类的一个指针
  CClientDC dc(pWnd);
  HDC hDC = dc.GetSafeHdc();         //HDC是Windows的一种数据类型,是设备描述句柄;
SetStretchBltMode(hDC, COLORONCOLOR);//去掉多余像素点,这块影响画质 -- HALFTONE
  StretchDIBits(hDC,
    0,
    0,
    drect.right, //显示窗口宽度
    drect.bottom, //显示窗口高度
    0,
    0,
    matSrc.cols,   //图像宽度
    matSrc.rows,   //图像高度
    matSrc.data,
    bitMapinfo,
    DIB_RGB_COLORS,
    SRCCOPY
  );


里面每个参数都讲到了,这样你就可以显示图片,如果搭配while就可以显示视频了,无论打开视频文件还是本地网络摄像头,如果你用opencv还可以在这里面进行图片处理了,处理后再显示出来,只需要更新那个Mat图像对象就够了,堪称一个模板了。如果你有特殊需求,只需要查看函数原型的参数说明,更改参数就好了。



对于新手来说,想要单纯的试验,可以倒推,直接调用StretchDIBits然后缺哪个参数,就生成哪个,这个倒推法也很有效,肯定难不到聪明你,祝你成功!

MFC多线程的应用及更新主界面的图片显示视频


MFC多线程的应用及更新主界面的图片显示视频






头像

snowcoal
  • MFC
  • vs2019
  • 多线程
  • opencv
  • 显示图片

收藏到我的私密空间

标题:MFC多线程的应用及更新主界面的图片显示视频

作者:花花世界

你暂未登录,请登录后才可收藏至您的私密空间 确认取消
雪炭网

键盘操作 更便捷 -雪炭网雪中送炭-乐趣无限

如果本站的内容有幸帮助到了您,建议您了解一下当页的广告内容哦,我们的进步离不开您的支持,Thank you~