想要实现的功能是模拟交叉口的红绿灯显示。具体思路是利用线程来改变控制四个方向每个方向3盏灯的灯色数组!然后定时每0.01秒刷新区域,根据数组重画区域。调试没有错误可是运行达不到预期效果。void CClientDlg::OnPaint() 
{
 .....
 for(int i=0;i<=3;i++)
   for(int j=0;j<=2;j++)
    if(m_lampColor[i][j]==1)
{
   brush.CreateSolidBrush(RGB(255, 0, 0)); //创建红色笔刷
   dcsample.SelectObject(&brush); //将笔刷选进设备描述表
   dcsample.Ellipse(lamp[i][j]); //画灯
   brush.DeleteObject(); //释放画笔
}
else if(m_lampColor[i][j]==2)
{
   brush.CreateSolidBrush(RGB(255, 255, 0));//创建黄色笔刷
   dcsample.SelectObject(&brush);  //将笔刷选进设备描述表
   dcsample.Ellipse(lamp[i][j]);  //画灯
   brush.DeleteObject();  //释放画笔
}
else if(m_lampColor[i][j]==3)
{
   brush.CreateSolidBrush(RGB(0, 255, 0));  //创建绿色笔刷
   dcsample.SelectObject(&brush);        //将笔刷选进设备描述表
   dcsample.Ellipse(lamp[i][j]);        //画灯
   brush.DeleteObject();        //释放画笔
}
SetTimer(1,10,NULL);
}void CClientDlg::OnTimer(UINT nIDEvent) 
{
// TODO: Add your message handler code here and/or call default
InvalidateRect(&RectText,FALSE);
CDialog::OnTimer(nIDEvent);
}WORD WINAPI CClientDlg::RecvProc(LPVOID lpParameter)
{
UINT m_circle=((PARAM*)lpParameter)->m_circle; // 得到主线程相位数
UINT m_phase=((PARAM*)lpParameter)->m_phase; 
UINT *m_lamp=((PARAM*)lpParameter)->m_lamp;  // 得到主线程的红绿灯控制数组
while(TRUE)
{
if(m_phase==2)
{
for(int i=0;i<4;i++)
for(int j=0;j<3;j++)
if((i==0||i==2)&&(j==0||j==1))
*(m_lamp+i*3+j)=1;
else
*(m_lamp+i*3+j)=3;
m_phase--;
}
else if(m_phase==1)
{
for(int i=0;i<4;i++)
for(int j=0;j<3;j++)
if((i==1||i==3)&&(j==0||j==1))
*(m_lamp+i*3+j)=1;
else
*(m_lamp+i*3+j)=3;
m_phase++;
}
Sleep(m_circle/2);
}
return 0;
}问题补充,能不能不用settimer()函数在主线程内更新,能不能在线程里面通知主线程重绘呢。

解决方案 »

  1.   

    你这个需求完全可以用OnTimer实现
    不用多线程
    更新界面用
    Invalidate
    绘图用双缓冲另外
    SetTimer不要放到OnPaint里
      

  2.   

    不好意思需要做些补充:我感觉这个必须用多线程,因为我的不是定时的更新,可能红灯时间是10秒黄灯时间是3秒绿灯时间是20秒,这个是由另一段函数代码生成的,就是说根据交通量来定。Invalidate 和 InvalidateRect 没有什么区别吧。还有我还真不明白双缓冲室什么,请详细解释一下谢谢
      

  3.   

    Invalidate是刷新整个区域
    InvalidateRect是刷新指定的矩形区域。双缓冲,内存DC;创建步骤:
    HDC dc; // 源DC
    ...
    HDC memDC = CreateCompatibleDC(dc); // 创建兼容DC
    HBITMAP hbmp = CreateCompatibleBitmap(memDC, WIDTH, HEIGHT); // 创建兼容位图
    SelectObject(HDC, hbmp);//载入位图,如果这里的HDC为memDC则是单色位图,如果为源DC,则为彩色位图。
    // 在memDC上绘图,DrawText,FillRect,...
    BitBlt(dc, 0, 0, WIDTH, HEIGHT, memDC, 0, 0, SRCCOPY); // 把memDC上的内容拷贝进目的DC,这里的目的DC就是源DC
    DeleteObject(hbmp);
    DeleteDC(memDC);
      

  4.   

    InvalidateRect(&RectText,FALSE);
    这里的RectText是你的绘图区域么?
    你在哪启动的线程
      

  5.   


    是的 是绘图区域,在OnInitDialog 中启动的线程
      

  6.   


    开几个定时器没关系,可是我试了一下
     
    m_nTimer = SetTimer(1, 2000, 0);void CMainFrame::OnTimer(UINT nIDEvent) 
    {
      ....
      KillTimer(m_nTimer); 
    }好像不可以 所以想用开线程的方式,有的交叉口不知道能有多少种变化,有可能开10个定时器,也有可能开20多个不如写在线程里面的方便
      

  7.   


    这个我就知道了,开始我也想用背景往上面贴的,可是灯的组合颜色太多了,后来想想还是画吧。现在的问题是我先在线程里面调用Invalidate 或者 InvalidateRect 等到颜色变的时候就更新一下。可以 ::PostMessage WMPAINT 但是想要Invalidate(FAULSE)的那种效果。 
      

  8.   

    把你的窗口指针作为参数传到线程里
    比如pWndMain
    然后pWndMain-〉Invalidate(FAULSE);
      

  9.   


    这个我尝试过,但是error C2039: 'Invalidate' : is not a member of 'HWND__'
      

  10.   

    把你的窗口指针作为参数传到线程里
    比如pWndMain
    然后pWndMain-〉Invalidate(FAULSE);这个我尝试过,但是error C2039: 'Invalidate' : is not a member of 'HWND__'你的 pWndMain 不是 CWnd* 而是 HWND ?
      

  11.   

    问题复杂化了。
    首先你的要求是0.01秒显示,而settimer的时间精度大约是55毫秒,达不到你的要求,建议你用线程来实现定时器,但不要那么多线程,一个就够了。精度就定成10毫秒。
    另外一点就是不明白你把精度设这么高是因为你要每10毫秒从另一个函数取一次灯的状态,还是想10毫秒涮新一次界面。因为你说达不到效果,但不知你想要什么效果。
    按我的理解,你无非是想从另一个函数取灯的状态(就是每个灯灭,亮,闪烁,时长),然后在界面动态显示(灯闪烁,关闭,长亮),没必要10毫秒涮新一次,红绿灯应该只有在闪烁时需要根据频率进行涮新,其它状态,只要在状态改变画一次就可以了(在OnPaint里根据状态画)。
    而且采用定时器无论采用哪种方式只要写一个就可以了,把时间精度设为最小的。
      

  12.   


    不好意思,我知道了是得到窗口的指针不是窗口的句柄,那我该如何得到窗口的指针呢? ::FromHandle()? 可是用什么变量来存储得到的窗口的指针呢?
      

  13.   


    你说的很有道理,我现在就是想用线程来实现,刷新的功能。SetTimer()不用了。现在又有一个新的问题要问你,闪烁怎么实现呢?改变灯色重新选择画刷重新画就可以可是闪烁呢?
      

  14.   

    在主窗口启动线程
    直接用this指针就可以
    你启动线程的函数怎么写的?
    应该可以可以带一个LPVIOD的参数
    直接传进就行
    然后强制类型转化一下就可以用了
      

  15.   


    // 这是定义LPVIOD 的结构体
    struct PARAM
    {
    UINT *m_lamp;
    UINT m_phase;
    UINT m_circle;
    CWnd*   m_pMain;
    };
    // 下面是启动线程的函数 PARAM *pRecvParam=new PARAM;
    pRecvParam->m_lamp=&m_lampColor[0][0];
    pRecvParam->m_phase=m_phase;
    pRecvParam->m_circle=m_circle;
    CWnd* m_pMain=GetDlgItem(IDD_CLIENT_DIALOG);
    HANDLE hThread=CreateThread(NULL,0,RecvProc,(LPVOID)pRecvParam,0,NULL);
    CloseHandle(hThread);DWORD WINAPI CClientDlg::RecvProc(LPVOID lpParameter)
    {
    UINT m_circle=((PARAM*)lpParameter)->m_circle; // 得到主线程相位数
    UINT m_phase=((PARAM*)lpParameter)->m_phase; 
    UINT *m_lamp=((PARAM*)lpParameter)->m_lamp;  // 得到主线程的红绿灯控制数组
    CWnd* m_pMain=((PARAM*)lpParameter)->m_pMain; //得到主窗口的指针
    while(TRUE)
    {
    if(m_phase==2)
    {
    for(int i=0;i<4;i++)
    for(int j=0;j<3;j++)
    if((i==0||i==2)&&(j==0||j==1))
    *(m_lamp+i*3+j)=1;
    else
    *(m_lamp+i*3+j)=3;
    m_phase--;
    }
    else if(m_phase==1)
    {
    for(int i=0;i<4;i++)
    for(int j=0;j<3;j++)
    if((i==1||i==3)&&(j==0||j==1))
    *(m_lamp+i*3+j)=1;
    else
    *(m_lamp+i*3+j)=3;
    m_phase++;
    }
    Sleep(1000);
    m_pMain->Invalidate(FALSE); // 更新主窗口
    }
    return 0;
    }编译没有错误,可是出现了非法的操作??
      

  16.   

    闪烁就要用定时器来做,因为MFC不是线程安全的,你不能在另一个线程里直接控制界面进行涮新,但一般人眼的分辨是有限的,精度不用那么高,定时器的精度足够了,当你在工作线程里发现需要进行闪烁时,启动界面定时器,间隔就取你闪烁的最小间隔(假定你的闪烁是变化的,开始慢,越往后越快),在定时器里改变灯的状态,调用Invalidate涮新界面,或者直接创建客户DC设备画图,这个要看你的线程处理速度,Invalidate()涮新是不确定的,只有在系统闪时才会去涮新。
    另外说一句,你的整个模型就是个状态机,你应该先画出状态变迁图,在灯的结构里定好灯的状态,所要做的就是进行灯的状态变迁,画都是表现方式。你现在把所有的东西揉到一起去写,逻辑上比较乱,如果是个项目的话,以后维护起来是很困难的。
      

  17.   

    pRecvParam->m_lamp=&m_lampColor[0][0];
    pRecvParam->m_phase=m_phase;
    pRecvParam->m_circle=m_circle;
    CWnd* m_pMain=GetDlgItem(IDD_CLIENT_DIALOG);
    =====================================================
    你这个位置写错了把
    最后一句是不是应该是
    pRecvParam->m_pMain=GetDlgItem(IDD_CLIENT_DIALOG);这种问题应该自己耐心调试一下
      

  18.   

    闪烁就要用定时器来做,因为MFC不是线程安全的,你不能在另一个线程里直接控制界面进行涮新难道我上面发的非法操作的原因是因为这样?我想跟您细聊一下,不会占用您太多的时间,我是做毕业设计的学生,可否加个QQ 我的QQ 号码:546105573 非常的感谢!
      

  19.   

    看你的代码,m_lampColor这个是你的灯结构,但看样子只是个状态值
    你可以定义个结构体
    typedef struct stLamp
    {
        UNIT    nState;   // 灯的状态:亮,灭,闪烁
        COLOREF nColor;   // 灯的颜色
         int     nTime;    // 闪烁时长
         int     nStep;    // 闪烁步长
    }LAMP;#define DIR      4  // 四个方向
    #define LAMP_NUM 2  // 每方向三个灯
    #define FLICK    200 // 200毫秒闪烁一次
    LAMP  g_tLamp[4][3];
    这样,你只要根据收到的函数,去改变这个结构体数组里对应的结构就行了
    界面部分,只是取结构体的值涮新,定时器里更改nStep步长变量控制闪烁频率