最近因为课程需要,自己设计了一些相对简单的绘制坐标和波形图的函数,这些函数不够理想,但是对于简单要求足够了,以后自己会逐渐的完善这些函数。这里先把他们放这里,以免找不到。
在MFC中,如果不是绘制动态波形图,一般都要在OnPaint或者OnDraw里面进行绘制,但是大家都知道,如果把一大串的绘图用的代码都放到OnPaint或者OnDraw会很纠结的。如果把绘图的代码整理起来,放到一个或者几个函数里面,这样会使得代码易懂且美观,另外最大的优点是绘图函数可以很好的复用,以后再用,只用改动极少的地方。这些绘图函数就是你的经验,就是你比别人快的地方。
我先阐述我的思路:首先,如果是绘制静态图,为了能够在移动窗口时图片依旧存在,我看到最多的方法是用双缓存技术,这个技术我就不解释了,百度上面多得是。绘制图形的代码会比较多,如果为了复用,只能把绘图分解,这在面向对象中经常用到,我要记住,大家也要记住。因为人与人的理解会不同,所以把动作分解的方式也肯定会不一样,我认为,只要目的达到了而且又没有特别明显的差距,什么样的方法都是OK的,不能因为他是大神,他是权威,他的就是最好的,大多时候是合适的才是最好的。过多的否定自己的想法会使得自己思想僵化,总是以为自己办不到,总以为别人的最好,我觉得这样不利于自己成长,不利于自己原创思想的迸发。对于绘图,首先要有笔吧,而笔呢,什么颜色?多粗?然后是你绘画在什么地方?比如画在纸上,你的纸张有多大?纸张的背景颜色什么?白的?黑的?。。。这些物质上的准备都是必须的吧?所以,第一个函数就是准备“笔”和“纸”的,如果有必要,自己还可以细分选择“笔”和”纸“的动作,这里我觉得我暂时用不到,我就不再细分下去了。剩下的事情就是真正的画图了,画得怎么样就考验个人能力了,就像画家之间的绘画能力一样,画得好,画得差都体现在这里。当然前面选择“笔”和选择“纸张”也考验人,图形的绘制考验人的审美能力和绘画能力,选择什么样的“笔”和“纸”自己定。对于绘制波形图,肯定先要绘制坐标框架,然后再绘制波形,因为如果先绘制波形,那么波形就会有可能被坐标框遮住一部分;同样如果要在图中有网格,那么肯定也是先绘制坐标框架再绘制网格最后绘制波形,这些和实际绘图有所区别。
现在开始代码。首先是在OnPaint里面,准备“笔”和“纸”

1 void DTTFDlg::OnPaint() 2 { 3 CPaintDC dc(this); // device context for painting 4 // TODO: 在此处添加消息处理程序代码 5 // 不为绘图消息调用 CDialog::OnPaint() 6 CWnd* pWnd; 7 CRect rc; 8 pWnd = GetDlgItem(IDC_STATIC_FFTPIC); 9 pWnd->GetWindowRect(rc); 10 CDC* pdc = pWnd->GetDC(); 11 StartDraw(rc,pdc,1); 12 }
准备“笔”和“纸”的函数,

1 void DTTFDlg::StartDraw(CRect rc, CDC* pdc, int flag) 2 { 3 CPen newPen; // 用于创建新画笔 4 CPen *pOldPen; // 用于存放旧画笔 5 CBrush newBrush; // 用于创建新画刷 6 CBrush *pOldBrush; // 用于存放旧画刷 7 8 int width = rc.Width(); 9 int height = rc.Height(); 10 11 //绘图前的准备,一般不要改 12 //******************************************************************* 13 CDC MemDC; //首先定义一个显示设备对象 14 CBitmap MemBitmap;//定义一个位图对象 15 //随后建立与屏幕显示兼容的内存显示设备 16 MemDC.CreateCompatibleDC(NULL); 17 //这时还不能绘图,因为没有地方画 ^_^ 18 //下面建立一个与屏幕显示兼容的位图,至于位图的大小嘛,可以用窗口的大小 19 MemBitmap.CreateCompatibleBitmap(pdc,width,height); 20 //将位图选入到内存显示设备中 21 //只有选入了位图的内存显示设备才有地方绘图,画到指定的位图上 22 CBitmap *pOldBit=MemDC.SelectObject(&MemBitmap); 23 //先用背景色将位图清除干净,这里我用的是白色作为背景 24 //你也可以用自己应该用的颜色 25 MemDC.FillSolidRect(0,0,width,height,RGB(0,0,0)); 26 //******************************************************************* 27 28 //按照自己的情况来选择背景颜色 29 //******************************************************************* 30 // 创建黑色新画刷 31 newBrush.CreateSolidBrush(RGB(0,0,0)); 32 pOldBrush = MemDC.SelectObject(&newBrush); 33 // 以黑色画刷为绘图控件填充黑色,形成黑色背景 34 MemDC.Rectangle(rc); 35 // 恢复旧画刷 36 MemDC.SelectObject(pOldBrush); 37 // 删除新画刷 38 newBrush.DeleteObject(); 39 // 创建实心画笔,粗度为1,颜色为绿色 40 newPen.CreatePen(PS_SOLID, 1, RGB(0,255,0)); 41 // 选择新画笔,并将旧画笔的指针保存到pOldPen 42 pOldPen = MemDC.SelectObject(&newPen); 43 //******************************************************************* 44 45 46 //放置要用到的绘图函数 47 //******************************************************************* 48 //绘制网格 49 DrawPic(rc, &MemDC,1); 50 //绘制图形 51 Plot(rc,&MemDC,1); 52 //******************************************************************* 53 54 55 //处理后事,一般不用改 56 //******************************************************************* 57 MemDC.SelectObject(pOldPen); // 恢复旧画笔 58 newPen.DeleteObject(); // 删除新画笔 59 //将内存中的图拷贝到屏幕上进行显示 60 pdc->BitBlt(0,0,width,height,&MemDC,0,0,SRCCOPY); 61 //绘图完成后的清理 62 MemBitmap.DeleteObject(); 63 MemDC.DeleteDC(); 64 //******************************************************************* 65 }
绘制网格,9列5行,

1 void DTTFDlg::DrawGrid(CRect rc, CDC* pdc, int flag) 2 { 3 //绘制网格的参数 4 int x = g; 5 int y = g; 6 int h = height - 2*g; 7 int w = width - 2*g; 8 int wg = (int)(w/10); 9 int hg = (int)(h/6); 10 int sg = (width - 2*g)/50; 11 12 int wi[10]; 13 int hi[5]; 14 int i; 15 //竖线 16 switch(w%10) 17 { 18 case 0: 19 for(i = 0; i < 10; i++) 20 wi[i] = wg; 21 break; 22 case 1: 23 for(i = 0; i < 10; i++) 24 { 25 if(i == 0) 26 wi[i] = wg+1; 27 else 28 wi[i] = wg; 29 } 30 break; 31 case 2: 32 for(i = 0; i < 10; i++) 33 { 34 if(i <= 1) 35 wi[i] = wg+1; 36 else 37 wi[i] = wg; 38 } 39 break; 40 case 3: 41 for(i = 0; i < 10; i++) 42 { 43 if(i <= 2) 44 wi[i] = wg+1; 45 else 46 wi[i] = wg; 47 } 48 break; 49 case 4: 50 for(i = 0; i < 10; i++) 51 { 52 if(i <= 3) 53 wi[i] = wg+1; 54 else 55 wi[i] = wg; 56 } 57 break; 58 case 5: 59 for(i = 0; i < 10; i++) 60 { 61 if(i <= 4) 62 wi[i] = wg+1; 63 else 64 wi[i] = wg; 65 } 66 break; 67 case 6: 68 for(i = 0; i < 10; i++) 69 { 70 if(i <= 5) 71 wi[i] = wg+1; 72 else 73 wi[i] = wg; 74 } 75 break; 76 case 7: 77 for(i = 0; i < 10; i++) 78 { 79 if(i <= 6) 80 wi[i] = wg+1; 81 else 82 wi[i] = wg; 83 } 84 break; 85 case 8: 86 for(i = 0; i < 10; i++) 87 { 88 if(i <= 7) 89 wi[i] = wg+1; 90 else 91 wi[i] = wg; 92 } 93 break; 94 case 9: 95 for(i = 0; i < 10; i++) 96 { 97 if(i <= 8) 98 wi[i] = wg+1; 99 else 100 wi[i] = wg; 101 } 102 break; 103 } 104 //横线 105 switch(h%6) 106 { 107 case 0: 108 for(i = 0; i < 5; i++) 109 hi[i] = hg; 110 break; 111 case 1: 112 for(i = 0; i < 5; i++) 113 { 114 if(i == 0) 115 hi[i] = hg+1; 116 else 117 hi[i] = hg; 118 } 119 break; 120 case 2: 121 for(i = 0; i < 5; i++) 122 { 123 if(i <= 1) 124 hi[i] = hg+1; 125 else 126 hi[i] = hg; 127 } 128 break; 129 case 3: 130 for(i = 0; i < 5; i++) 131 { 132 if(i <= 2) 133 hi[i] = hg+1; 134 else 135 hi[i] = hg; 136 } 137 break; 138 case 4: 139 for(i = 0; i < 5; i++) 140 { 141 if(i <= 3) 142 hi[i] = hg+1; 143 else 144 hi[i] = hg; 145 } 146 break; 147 case 5: 148 for(i = 0; i < 5; i++) 149 { 150 if(i <= 4) 151 hi[i] = hg+1; 152 else 153 hi[i] = hg; 154 } 155 break; 156 157 } 158 159 //刻度 160 int temp1; 161 int temp2; 162 int j = 0; 163 int gi[50]; 164 for(i = 0; i < 10; i++) 165 { 166 temp1 = wi[i]/5; 167 temp2 = wi[i]%5; 168 switch(temp2) 169 { 170 case 0: 171 for(j = 0; j < 5; j++) 172 gi[5*i+j] = temp1; 173 break; 174 case 1: 175 for(j = 0; j < 5; j++) 176 { 177 if(j == 0) 178 gi[5*i+j] = temp1 + 1; 179 else 180 gi[5*i+j] = temp1; 181 } 182 break; 183 case 2: 184 for(j = 0; j < 5; j++) 185 { 186 if(j <= 1) 187 gi[5*i+j] = temp1 + 1; 188 else 189 gi[5*i+j] = temp1; 190 } 191 break; 192 case 3: 193 for(j = 0; j < 5; j++) 194 { 195 if(j <= 2) 196 gi[5*i+j] = temp1 + 1; 197 else 198 gi[5*i+j] = temp1; 199 } 200 break; 201 case 4: 202 for(j = 0; j < 5; j++) 203 { 204 if(j <= 3) 205 gi[5*i+j] = temp1 + 1; 206 else 207 gi[5*i+j] = temp1; 208 } 209 break; 210 } 211 } 212 213 214 CPen newPen; 215 CPen* pOldPen; 216 217 if(flag == 0) 218 newPen.CreatePen(PS_DOT,1,RGB(128,128,128)); //网格的颜色为灰色 219 else 220 newPen.CreatePen(PS_DOT,1,RGB(169,169,169)); //网格的颜色为深灰色 221 pOldPen = pdc->SelectObject(&newPen); //选择画笔 222 223 //绘制竖线 224 for(int i = 0; i < 9 ; i++) 225 { 226 x = x + wi[i]; 227 y = y + hi[i]; 228 //绘制竖虚线 229 pdc->MoveTo(x,g); 230 pdc->LineTo(x,height-g); 231 //绘制横虚线 232 if(i < 5 && i != 2) 233 { 234 pdc->MoveTo(g,y); 235 pdc->LineTo(width-g,y); 236 } 237 } 238 239 //销毁画笔 240 newPen.DeleteObject(); 241 242 if(flag == 0) 243 newPen.CreatePen(PS_SOLID,1,RGB(255,255,255)); //定义画笔,颜色为白色 244 else 245 newPen.CreatePen(PS_SOLID,1,RGB(0,0,0)); //定义画笔,颜色为黑色 246 pdc->SelectObject(newPen); //选择新画笔 247 248 //绘制坐标轴的边框 249 x = g; 250 y = g; 251 pdc->MoveTo(g,g); 252 pdc->LineTo(width-g,g); 253 pdc->MoveTo(width-g,g); 254 pdc->LineTo(width-g,height-g); 255 pdc->MoveTo(width-g,height-g); 256 pdc->LineTo(g,height-g); 257 pdc->MoveTo(g,height-g); 258 pdc->LineTo(g,g); 259 //绘制x轴轴线 260 pdc->MoveTo(x,y+3*hg); 261 pdc->LineTo(width-g,y+3*hg); 262 263 //绘制刻度 264 int x1 = g; 265 int yv = y+3*hg; 266 for(int i = 0; i < 50; i++) 267 { 268 x1 = x1 + gi[i]; 269 pdc->MoveTo(x1,yv+3); 270 pdc->LineTo(x1,yv-2); 271 } 272 273 pdc->SelectObject(pOldPen); 274 newPen.DeleteObject(); 275 }
绘制波形图

1 CPen newPen; 2 CPen* pOldPen; 3 newPen.CreatePen(PS_SOLID,1,RGB(178,34,34)); //绘制曲线的颜色,火砖色 4 pOldPen = pdc->SelectObject(&newPen); 5 6 int height = rc.Height(); 7 int width = rc.Width(); 8 int w = width - 2*g; 9 int h = height - 2*g; 10 11 int x = g; 12 int y = g; 13 int i; 14 15 double W = double(w*100)/100.0; 16 double H = double(h*100)/100.0; 17 18 pdc->MoveTo(g,(int)(height/2)); 19 20 for(i = 0; i < m_PointNum;i++) 21 { 22 x = g + int(W*xdata.retrieveAt(i)/xdata.maxValue()); //xdata要绘图的数据 23 y = (int)(height/2)-int(H/2*ydata.retrieveAt(i)/ydata.maxValue());//ydata要绘图的数据 24 pdc->LineTo(x,y); 25 } 26 pdc->SelectObject(pOldPen); 27 newPen.DeleteObject();
上面出现的变量,

1 //边框到窗口的距离 2 int g; 3 //用于存储数据,arrayListType我自己写的 4 arrayListType<double> xdata; 5 arrayListType<double> ydata; 6 // 用于记录坐标点个数,在视图类构造函数中初始化为0。 7 int m_PointNum;