1、目录前言0实验目的11 窗体设计12 实现命令基类及各种命令类12.1实现命令基类22.2实现直线、矩形、圆弧绘制命令22.3实现平移、旋转操作命令类73实现图元选取功能124窗口的重绘及文件的存取164.1窗口的重绘164.2文件的存取175 程序运行效果图176 结束语18参考文献18前言 本绘图工具可以绘制直线、矩形、圆弧3中简单图形,并可以对这3种图形进行选中、平移旋转3种操作。该绘图工具是基于视图文档的应用程序,它还提供了完整的储存与加载文件的功能。虽然改绘图工具所实现的能够绘制的图形类型以及对这些图形的操作很有限,但是该绘图工具实现了一个完整的绘图工具的框架。实验目的(1) 建立视
2、图文档的程序框架。(2) 创建工具栏。(3) 实现逻辑坐标类,该类为整个程序提供了逻辑坐标位置相关操作的支持。(4) 使用设备坏境变量的各种绘制功能。(5) 使用设备环境变量的不同绘制模式。(6) 实现文件的流式储存与加载功能。(7) 实现在状态栏显示提示信息功能。1 窗体设计该绘图工具是基于视图文档结构的应用程序。首先利用MFC AppWizard建立视图文档的应用程序框架,再为其添加必需的其它资源。利用MFC AppWizard生成绘制工具应用程序框架,打开MFC AppWizard(exe),新建Mypiant工程。利用资源编辑器添加ID为ID_ENTITY的工具栏,添加绘制直线、矩形、
3、圆弧按钮。利用资源编辑器添加ID为ID_OPTION的工具栏,添加图元选择、平移、旋转按钮。2 实现命令基类及各种命令类该绘图工具将用户对各种图元的绘制与操作都视为一系列“命令”;对各种图元的绘制与操作工作,都转化为对命令的执行。2.1 实现命令基类选择主菜单上的“插入”,选择“类”,即弹出“新建类”对话框。在“类的类型”下拉菜单中选择Generic Class命令,在“名称”文本框中输入CCommand。单击“确认”按钮,向工程添加命令基类CCommand类。在CCommand类中,对所有命令的共有特征进行抽象,并定义成为虚函数。其实现代码如下:class CCommandprotected
4、:intm_nStep ; / 命令操作步public:CCommand() CCommand() virtual int GetType() = 0;/ 返回命令类型 ECommandTypevirtual int OnLButtonDown(UINT nFlags, const Position& pos) = 0 ;virtual int OnMouseMove(UINT nFlags, const Position& pos) = 0 ;virtual int OnRButtonDown(UINT nFlags, const Position& pos) = 0 ;virtual in
5、t Cancel() = 0 ; /取消操作 ;2.2 实现直线、矩形、圆弧绘制命令以直线绘制为例,向工程添加直线绘制命令CCreateLine类,如下图所示。在CCreateLine类中,覆盖其父类CCommand类中的所有虚函数,并定义两个绘制直线所必须的成员变量:直线起点坐标和直线终点坐标。CCreateLine类声明如下:class CCreateLine : public CCommandprivate:Position m_begin;/ 直线的起点Position m_end;/ 直线的终点 public:CCreateLine() ;CCreateLine() ;intGetT
6、ype();intOnLButtonDown(UINT nFlags, const Position& pos) ;intOnMouseMove(UINT nFlags, const Position& pos) ;intOnRButtonDown(UINT nFlags, const Position& pos) ;intCancel() ; ;在OnLButtonDown函数中,对计数器m_nStep进行判断。如果m_nStep值为1,说明当前在进行直线绘制的第一步操作;将当前坐标保存到成员变量m_begin中。如果m_nStep值为2,那么说明当前正在进行直线绘制的第二步操作;首先,得到
7、视图类的设备坏境变量指针;其次,擦除在拖动状态时显示的最后一条橡皮线;最后绘制最终的俄直线。其实现代码如下:/响应鼠标左键单击操作intCCreateLine:OnLButtonDown(UINT nFlags, const Position& pos) m_nStep +;/ 每次单击鼠标左键时操作步加 1 switch(m_nStep) / 根据操作步执行相应的操作case 1: m_begin = m_end = pos; /保存直线起点:Prompt(请再点击鼠标,选则线段末尾坐标);break;case 2:CDC*pDC = g_pView-GetDC(); / 得到设备环境指针
8、/ 擦除在拖动状态时显示的最后一条线CLine*pTempLine = new CLine(m_begin,m_end); pTempLine-Draw(pDC, dmDrag);delete pTempLine;/ 如果在按鼠标左键的过程中同时按下了Shift键,/ 那么根据鼠标单击位置绘制水平线或竖直线if( nFlags & MK_SHIFT ) double dx = pos.x - m_begin.x;double dy = pos.y - m_begin.y;if(fabs(dx) Draw(pDC,dmNormal);/ 绘制直线g_pDoc-m_EntityList.AddTa
9、il(pNewLine);/ 将直线指针添加到图元链表g_pDoc-SetModifiedFlag(TRUE);g_pView-ReleaseDC(pDC); / 释放设备环境指针m_nStep = 0;break;return 0;在OnMouseMove函数中,对计数器m_nStep的值进行判断。如果m_nStep的值为0,则仅显示提示信息;如果m_nStep的值为1,首先擦除上一次拖动过程中绘制的橡皮线;其次绘制新的橡皮线。其实现代码如下:/响应鼠标移动操作intCCreateLine:OnMouseMove(UINT nFlags, const Position& pos)static
10、int nPreRefresh = g_nRefresh; BOOLbRefresh = FALSE; intnCurRefresh = g_nRefresh; if(nCurRefresh != nPreRefresh) bRefresh = TRUE;nPreRefresh = nCurRefresh; switch(m_nStep)case 0:Prompt(请输入直线的起点:); /显示提示信息break;case 1:Position prePos, curPos;prePos = m_end;/ 获得鼠标所在的前一个位置/ 如果在按鼠标左键的过程中同时按下了Shift键,/ 那么根
11、据鼠标单击位置绘制水平线或竖直线if( nFlags & MK_SHIFT )double dx = pos.x - m_begin.x;double dy = pos.y - m_begin.y;if(fabs(dx)=fabs(dy)curPos.Set(pos.x,m_begin.y);elsecurPos.Set(m_begin.x, pos.y);elsecurPos = pos;CDC*pDC = g_pView-GetDC(); / 得到设备环境指针 / 创建临时对象擦除上一条橡皮线CLine*pTempLine1 = new CLine(m_begin, prePos);if(
12、!bRefresh) pTempLine1-Draw(pDC, dmDrag);delete pTempLine1; / 创建临时对象,根据当前位置绘制一条橡皮线CLine*pTempLine2 = new CLine(m_begin, curPos);pTempLine2-Draw(pDC, dmDrag);delete pTempLine2; g_pView-ReleaseDC(pDC); / 释放设备环境指针m_end = curPos; / 将当前位置设置为直线终点,以备下一次鼠标移动时用break;return 0;在OnRButtonDown函数中,完成对绘制命令的取消工作。首先,
13、对计数器m_nStep的值进行判断,如果m_nStep的值不为1,则将m_nStep的值设置为0;如果m_nStep的值为2,则擦除上一次拖动时所绘制的橡皮线,释放得到的设备坏境变量指针,m_nStep的值为0其实现代码如下:intCCreateLine:OnRButtonDown(UINT nFlags, const Position& pos) / 如果当前的操作步为 1 ,那么要在结束本次操作前擦除上次鼠标移动时绘制的橡皮线if(m_nStep = 1) CDC*pDC = g_pView-GetDC();/ 得到设备环境指针PositionprePos = m_end;/ 获得鼠标所在
14、的前一个位置CLine*pTempLine = new CLine(m_begin, prePos); pTempLine-Draw(pDC, dmDrag);/ 擦除上一次绘制的橡皮线delete pTempLine;g_pView-ReleaseDC(pDC);/ 释放设备环境指针:Prompt(取消操作);m_nStep = 0;/ 将操作步重置为 0 return 0;函数Cancel的功能与OnRbuttonDown的功能相同,均为取消直线绘制操作;不同的是OnRButtonDown函数是通过鼠标右键单击调用的,而Cancel函数是通过其他方式被调用的。其实现代码如下:int CCr
15、eateLine:Cancel()/ 如果当前的操作步为 1 ,那么要在结束本次操作前擦除上次鼠标移动时绘制的橡皮线if(m_nStep = 1) CDC* pDC = g_pView-GetDC(); / 得到设备环境指针Position prePos = m_end; / 获得鼠标所在的前一个位置CLine* pTempLine = new CLine(m_begin, prePos); pTempLine-Draw(pDC, dmDrag); / 擦除上一次绘制的橡皮线delete pTempLine;g_pView-ReleaseDC(pDC); / 释放设备环境指针m_nStep =
16、 0; / 将操作步重置为 0 :Prompt(就绪); / 等待提示新类型的命令操作return 0 ;2.3 实现平移、旋转操作命令类以平移为例,向工程添加平移图元命令CMove类。在CMove类中,覆盖其父类CCommand类中的所有虚函数,并定义两个图元平移所必须的成员变量:基准点坐标和目标点坐标。CMove类声明如下:class CMove : public CCommandprivate:Position m_basePos;Position m_desPos;public:CMove() ;CMove() ;intGetType();intOnLButtonDown(UINT n
17、Flags, const Position& pos) ;intOnMouseMove(UINT nFlags, const Position& pos) ;intOnRButtonDown(UINT nFlags, const Position& pos) ;intCancel() ; ;在OnLButtonDown函数中,对计数器m_nStep进行判断。如果m_NStep值为1,说明当前正在进行图元平移操作的第一步操作;将当前坐标保存到成员变量m_nStep中。如果m_nStep值为2,则说明当前正在进行图元平移操作的第二步操作;首先,得到视图类的设备环境变量指针;其次,擦除在拖动状态时显
18、示的最后橡皮线;最后,绘制最终的图元。其实现代码如下:intCMove:OnLButtonDown(UINT nFlags, const Position& pos) m_nStep + ;switch(m_nStep)case 1:m_basePos = m_desPos = pos;:Prompt(请输入移动的目标点:单击鼠标右键取消) ;break;case 2:m_desPos = pos;CDC*pDC = g_pView-GetDC();/ 获得视类的设备环境指针CLine*pTempLine = new CLine(m_basePos, m_desPos);pTempLine-D
19、raw(pDC, dmDrag);delete pTempLine;int i, n;for(n = g_pDoc-m_selectArray.GetSize(), i = 0; i m_selectArrayi;pEntity-Draw(pDC,dmInvalid);/ 清除原来位置上的图元pEntity-Move(m_basePos, m_desPos);/ 将图元移动到目标位置pEntity-Draw(pDC,dmNormal);/ 在目标位置上绘制图元g_pDoc-m_selectArray.RemoveAll();/ 清空选择集g_pDoc-SetModifiedFlag(TRUE)
20、;/ 标志文档数据已被修改g_pView-ReleaseDC(pDC);/ 释放视类的设备环境指针m_nStep = 0; break;default:break;return 0;在OnMouseMove函数中,对计数器m_nStep的值进行判断。如果m_nStep的值为0,则仅显示提示信息;如果m_nStep的值为1,首先,擦除上一次拖动过程中绘制的橡皮线;其次,绘制新的橡皮线。其实现代码如下:intCMove:OnMouseMove(UINT nFlags, const Position& pos)staticint nPreRefresh = g_nRefresh; BOOLbRefr
21、esh = FALSE; intnCurRefresh = g_nRefresh; if(nCurRefresh != nPreRefresh) bRefresh = TRUE;nPreRefresh = nCurRefresh; switch(m_nStep)case 0:Prompt(请输入移动的起始点:) ;break;case 1:PositionprePos, curPos;prePos = m_desPos;/ 获得上一个目标位置curPos = pos;/ 得到当前位置CDC*pDC = g_pView-GetDC(); if(!bRefresh)CLine*pTempLine1
22、 = new CLine(m_basePos, prePos);pTempLine1-Draw(pDC, dmDrag);delete pTempLine1;/ 在当前位置绘制橡皮线CLine*pTempLine2 = new CLine(m_basePos, curPos);pTempLine2-Draw(pDC, dmDrag);delete pTempLine2;/ 根据当前位置给出选中图元的实时位置int i, n;for(n = g_pDoc-m_selectArray.GetSize(), i = 0; i m_selectArrayi;pEntity-Draw(pDC,dmSel
23、ect);/ 如果在操作过程中窗口没有被刷新,则要清除上一个位置上绘制的图元if(!bRefresh) CEntity*pCopyEntity1 = pEntity-Copy();/ 得到图元的拷贝pCopyEntity1-Move(m_basePos, prePos);/ 将拷贝移动到上一个位置pCopyEntity1-Draw(pDC,dmDrag);/ 对在上一个位置对拷贝进行重画delete pCopyEntity1;/ 删除临时拷贝/ 在当前位置上绘制图元CEntity*pCopyEntity2 = pEntity-Copy();/ 得到图元的拷贝pCopyEntity2-Move(
24、m_basePos, curPos);/ 将拷贝移动到当前位置pCopyEntity2-Draw(pDC,dmDrag);/ 对当前位置绘制拷贝delete pCopyEntity2; / 删除临时拷贝 g_pView-ReleaseDC(pDC);m_desPos = pos; / 将目标设置为当前位置default:break;return 0;在OnRbuttonDown函数中,完成对图元平移操作命令的取消工作。首先,对计数器m_nStep的值进行判断,如果m_nStep的值不为1,则将m_nStep的值设置为0;如果m_nStep的值为1,则擦除上一次拖动是所绘制的橡皮线,释放得到的设
25、备环境变量指针,并将m_nStep的值设置为0。其实现代码如下:intCMove:OnRButtonDown(UINT nFlags, const Position& pos) PositionprePos = m_desPos; / 得到上一个鼠标位置if(m_nStep = 1)CDC*pDC = g_pView-GetDC(); / 清除上一个绘制的橡皮线CLine*pTempLine = new CLine(m_basePos, prePos);pTempLine-Draw(pDC, dmDrag);deletepTempLine;int i, n;for(n = g_pDoc-m_s
26、electArray.GetSize(), i = 0; i m_selectArrayi;/ 清除上一次绘制的临时对象CEntity*pCopyEntity = pEntity-Copy();pCopyEntity-Move(m_basePos, prePos);pCopyEntity-Draw(pDC,dmDrag);deletepCopyEntity; / 重新绘制选择集中的对象pEntity-Draw(pDC, dmSelect); / redraw the selected entityg_pView-ReleaseDC(pDC); / dont forget thism_nStep
27、 = 0;:Prompt(请输入移动的起始点:) ;return 0;函数Cancel的功能与OnRButtonDown的功能相同,均为取消图元平移操作;不同的是OnRButtonDown函数是通过鼠标右键单击调用的,而Cancel函数是通过其他方式被调用的。其实现代码如下:int CMove:Cancel()PositionprePos = m_desPos; / 得到上一个鼠标位置if(m_nStep = 1)CDC*pDC = g_pView-GetDC();/ 清除上一个绘制的橡皮线CLine*pTempLine = new CLine(m_basePos, prePos);pTemp
28、Line-Draw(pDC, dmDrag);deletepTempLine;int i, n;for(n = g_pDoc-m_selectArray.GetSize(), i = 0; i m_selectArrayi;/ 清除上一次绘制的临时对象CEntity*pCopyEntity = pEntity-Copy();pCopyEntity-Move(m_basePos, prePos);pCopyEntity-Draw(pDC,dmDrag);deletepCopyEntity; / 重新绘制选择集中的对象pEntity-Draw(pDC, dmSelect); g_pView-Rel
29、easeDC(pDC); m_nStep = 0;/ 将操作步重置为 0 :Prompt(就绪);/ return 0;3 实现图元选取功能为菜单项ID_OPTION_PICK的COMMAND消息添加响应函数OnOptionPick,在该函数中将当前操作命令指针m_pCmd设置为空。其实现代码如下:void CMyPaintView:OnOptionPick() if(m_pCmd)delete m_pCmd;m_pCmd = NULL;为菜单项ID_OPTION_PICK的UPDATE_COMMAND_UI消息添加响应函数OnUpdateOptionPick。在该函数中修改菜单项ID_OPT
30、ION_PICK的选中标识。其实现代码如下:void CMyPaintView:OnUpdateOptionPick(CCmdUI* pCmdUI) pCmdUI-SetCheck(m_pCmd = NULL ? 1 : 0) ;为文档类CMyPaint类添加成员函数OnMouseMove,在其中添加判断是否有图元被拾取到的功能。在函数OnMouseMove中,遍历图元链表,依次调用每个图元的Pick成员函数,如果发现有图元被拾取到,则将该图元放到选择集中。其实现代码如下:void CMyPaintDoc:OnMouseMove(UINT nFlags, const Position& pos
31、)if(m_EntityList.GetCount() = 0)return;:Prompt(拾取图元);BOOLbPicked = FALSE;CEntity*pickedEntity = NULL;POSITIONposition = m_EntityList.GetHeadPosition();while(position != NULL)CEntity*pEntity = (CEntity *)m_EntityList.GetNext(position);double curRadius = PICK_RADIUS / g_pView-GetScale(); if( pEntity-P
32、ick(pos, curRadius) ) bPicked = TRUE;pickedEntity = pEntity;break;CDC*pDC = g_pView-GetDC(); / 得到视的设备环境指针if( bPicked ) / 如果某个图元被拾取到if( m_pPmtEntity )m_pPmtEntity-Draw( pDC, dmNormal ); m_pPmtEntity = NULL;m_pPmtEntity = pickedEntity ; if( ! IsSelected(m_pPmtEntity) ) /设置光标状态;m_pPmtEntity-LoadPmtCurs
33、or();m_pPmtEntity-Draw( pDC, dmPrompt );/ 如果提示图元已存在于选择集中,那么将它恢复为空else m_pPmtEntity = NULL;else / 如果没有图元被拾取到if( m_pPmtEntity ) m_pPmtEntity-Draw( pDC, dmNormal ); m_pPmtEntity = NULL; g_pView-ReleaseDC(pDC); / 释放视的设备环境指针void CMyPaintDoc:OnLButtonDown(UINT nFlags, const Position& pos)CDC*pDC = g_pView
34、-GetDC() ;/ 得到视的设备环境指针if(m_pPmtEntity) if( !(nFlags & MK_CONTROL) )/ 如果没有按下Ctrl键,则首先清空选择集RemoveAllSelected();m_pPmtEntity-Draw(pDC,dmSelect);/ 将图元绘制为选中状态m_selectArray.Add(m_pPmtEntity);/ 将图元放入选择集中elseif( !(nFlags & MK_CONTROL) )/ 如果没有按下Ctrl键,则清空选择集RemoveAllSelected();m_pPmtEntity = NULL;/ 将提示图元对象设置为
35、空g_pView-ReleaseDC(pDC);/ 释放视的设备环境指针为文档类CMyPaintDoc添加成员函数OnLButtonDown,在其中进行选择图元的操作。其实现代码如下:void CMyPaintDoc:OnLButtonDown(UINT nFlags, const Position& pos)CDC*pDC = g_pView-GetDC() ;/ 得到视的设备环境指针if(m_pPmtEntity) if( !(nFlags & MK_CONTROL) )/ 如果没有按下Ctrl键,则首先清空选择集RemoveAllSelected();m_pPmtEntity-Draw(
36、pDC,dmSelect);/ 将图元绘制为选中状态m_selectArray.Add(m_pPmtEntity);/ 将图元放入选择集中elseif( !(nFlags & MK_CONTROL) )/ 如果没有按下Ctrl键,则清空选择集RemoveAllSelected();m_pPmtEntity = NULL;/ 将提示图元对象设置为空g_pView-ReleaseDC(pDC);/ 释放视的设备环境指针为视图类CMyPaintDoc添加成员函数RemoveAllSelected,在其中完成清空所有选择集的操作。其实现代码如下:void CMyPaintDoc:RemoveAllSe
37、lected()/ 首先选择集中的元素绘制为正常状态,然后清空选择集CDC*pDC = g_pView-GetDC();for( int i = 0 ; i Draw(pDC, dmNormal) ;m_selectArray.RemoveAll() ;g_pView-ReleaseDC(pDC);为视图类CMyPaintDoc添加成员函数IsSelected,在其中判断某一个图元是否已经在选择集中。其实现代码如下:BOOL CMyPaintDoc:IsSelected(CEntity* pEntity)/ 判断图元对象是否已经在选择集中if( pEntity )for( int i = 0 ; i Draw(pDC);在文档类中的Draw函数中,依次调用各种图元的Draw函数,完成重画窗口的功能。其实现代码如下:void CMyPaintDoc:Draw(CDC *pDC)/ 绘制链表中的图元POSITIONpos = m_EntityList.GetHeadPosition();while(pos!
版权声明:以上文章中所选用的图片及文字来源于网络以及用户投稿,由于未联系到知识产权人或未发现有关知识产权的登记,如有知识产权人并不愿意我们使用,如有侵权请立即联系:2622162128@qq.com ,我们立即下架或删除。
Copyright© 2022-2024 www.wodocx.com ,All Rights Reserved |陕ICP备19002583号-1
陕公网安备 61072602000132号 违法和不良信息举报:0916-4228922