想要实现的功能是模拟交叉口的红绿灯显示。具体思路是利用线程来改变控制四个方向每个方向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()函数在主线程内更新,能不能在线程里面通知主线程重绘呢。
{
.....
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()函数在主线程内更新,能不能在线程里面通知主线程重绘呢。
解决方案 »
- 怎样刷新子对话框上的按钮
- DoModal窗口点击确定后,崩溃...
- SetForegroundWindow()没法搞开魔兽
- 24点游戏请教大家
- [第一次写重叠I/O]:服务器接收到客户端发来的信息后,如何向客户端回送收到的信息?
- 求救!如何在server端接受10个以上的socket报文!
- MFC:基于单文档,怎么改变客户区中对话框的大小和位置(停靠位置)?
- 请教 矢量字体??
- 怎样使我的程序永远处于桌面的最上方,即使用了桌面刷新,还能显示在桌面上
- 如何做一个定时器?
- CLoginDlg dlg;m_pMainWnd = &dlg;编译出错?
- 无法打开输入文件:dxree9.lib是什么原因啊?多谢了
不用多线程
更新界面用
Invalidate
绘图用双缓冲另外
SetTimer不要放到OnPaint里
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);
这里的RectText是你的绘图区域么?
你在哪启动的线程
是的 是绘图区域,在OnInitDialog 中启动的线程
开几个定时器没关系,可是我试了一下
m_nTimer = SetTimer(1, 2000, 0);void CMainFrame::OnTimer(UINT nIDEvent)
{
....
KillTimer(m_nTimer);
}好像不可以 所以想用开线程的方式,有的交叉口不知道能有多少种变化,有可能开10个定时器,也有可能开20多个不如写在线程里面的方便
这个我就知道了,开始我也想用背景往上面贴的,可是灯的组合颜色太多了,后来想想还是画吧。现在的问题是我先在线程里面调用Invalidate 或者 InvalidateRect 等到颜色变的时候就更新一下。可以 ::PostMessage WMPAINT 但是想要Invalidate(FAULSE)的那种效果。
比如pWndMain
然后pWndMain-〉Invalidate(FAULSE);
这个我尝试过,但是error C2039: 'Invalidate' : is not a member of 'HWND__'
比如pWndMain
然后pWndMain-〉Invalidate(FAULSE);这个我尝试过,但是error C2039: 'Invalidate' : is not a member of 'HWND__'你的 pWndMain 不是 CWnd* 而是 HWND ?
首先你的要求是0.01秒显示,而settimer的时间精度大约是55毫秒,达不到你的要求,建议你用线程来实现定时器,但不要那么多线程,一个就够了。精度就定成10毫秒。
另外一点就是不明白你把精度设这么高是因为你要每10毫秒从另一个函数取一次灯的状态,还是想10毫秒涮新一次界面。因为你说达不到效果,但不知你想要什么效果。
按我的理解,你无非是想从另一个函数取灯的状态(就是每个灯灭,亮,闪烁,时长),然后在界面动态显示(灯闪烁,关闭,长亮),没必要10毫秒涮新一次,红绿灯应该只有在闪烁时需要根据频率进行涮新,其它状态,只要在状态改变画一次就可以了(在OnPaint里根据状态画)。
而且采用定时器无论采用哪种方式只要写一个就可以了,把时间精度设为最小的。
不好意思,我知道了是得到窗口的指针不是窗口的句柄,那我该如何得到窗口的指针呢? ::FromHandle()? 可是用什么变量来存储得到的窗口的指针呢?
你说的很有道理,我现在就是想用线程来实现,刷新的功能。SetTimer()不用了。现在又有一个新的问题要问你,闪烁怎么实现呢?改变灯色重新选择画刷重新画就可以可是闪烁呢?
直接用this指针就可以
你启动线程的函数怎么写的?
应该可以可以带一个LPVIOD的参数
直接传进就行
然后强制类型转化一下就可以用了
// 这是定义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;
}编译没有错误,可是出现了非法的操作??
另外说一句,你的整个模型就是个状态机,你应该先画出状态变迁图,在灯的结构里定好灯的状态,所要做的就是进行灯的状态变迁,画都是表现方式。你现在把所有的东西揉到一起去写,逻辑上比较乱,如果是个项目的话,以后维护起来是很困难的。
pRecvParam->m_phase=m_phase;
pRecvParam->m_circle=m_circle;
CWnd* m_pMain=GetDlgItem(IDD_CLIENT_DIALOG);
=====================================================
你这个位置写错了把
最后一句是不是应该是
pRecvParam->m_pMain=GetDlgItem(IDD_CLIENT_DIALOG);这种问题应该自己耐心调试一下
你可以定义个结构体
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步长变量控制闪烁频率