广州大学学生实验报告
开课实验室:计算机科学与工程实验(电子楼418A) 2023年5月25日
学院 | 计算机科学与网络工程学院 | 年级、专业、班 | 姓名 | 学号 | ||||
实验课程 | 面向对象程序设计 | 成绩 | ||||||
实验项目 | 实验五 综合应用程序 | 指导老师 | ||||||
一、实验目的: 本实验是对前面所学知识的总结,通过一个比较完整的应用程序的设计,将学过的知识连贯起来,掌握开发一个实际应用程序的步骤,同时学会使用开发工具实现界面友好的应用程序。并通过本实验,掌握如何运用面向对象技术对具体的应用系统进行分析和设计。 1、类和对象的定义、对象的初始化和使用 2、面向对象的特征的应用:封装、继承和多态 3、可视化程序设计中资源的运用 4、Windows的消息传递机制 5、MFC类库的使用 6、算法的使用 二、使用仪器、器材 微机一台 操作系统:WinXP 编程软件:C++ 三、实验内容及原理 利用文档/视图结构创建一个绘图板,实现用鼠标画线等功能,该画板至少要实现以下3个功能:
(2)可以修改所绘制图形的线宽和颜色,效果如下图。 (3)可以存储所绘制的图形。 // MyPaintView.h: CMyPaintView 类的接口 // #pragma once class CMyPaintView : public CView { protected: // 仅从序列化创建 CMyPaintView() noexcept; DECLARE_DYNCREATE(CMyPaintView) // 特性 public: CMyPaintDoc* GetDocument() const; // 操作 public: int m_PenSize; COLORREF m_PenColor, m_BrushColor; CPoint m_PointBegin, m_PointEnd; enum class DrawType { LineSetgment, Circle, Rectangle, Ellips, Pencil, Eraser }m_DrawType; // 重写 public: virtual void OnDraw(CDC* pDC); // 重写以绘制该视图 virtual BOOL PreCreateWindow(CREATESTRUCT& cs); protected: virtual BOOL OnPreparePrinting(CPrintInfo* pInfo); virtual void OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo); virtual void OnEndPrinting(CDC* pDC, CPrintInfo* pInfo); // 实现 public: virtual ~CMyPaintView(); #ifdef _DEBUG virtual void AssertValid() const; virtual void Dump(CDumpContext& dc) const; #endif protected: // 生成的消息映射函数 protected: DECLARE_MESSAGE_MAP() public: afx_msg void OnLButtonDown(UINT nFlags, CPoint point); afx_msg void onm ouseMove(UINT nFlags, CPoint point); afx_msg void OnLButtonUp(UINT nFlags, CPoint point); afx_msg void OnDrawLineSegment(); afx_msg void OnDrawRectangle(); afx_msg void OnDrawCircle(); afx_msg void OnDrawEllips(); afx_msg void OnSetPencil(); afx_msg void OnSetEraser(); afx_msg void OnSetColor(); afx_msg void OnSetPenSize(); afx_msg void OnFileSave(); }; #ifndef _DEBUG // MyPaintView.cpp 中的调试版本 inline CMyPaintDoc* CMyPaintView::GetDocument() const { return reinterpret_cast<CMyPaintDoc*>(m_pDocument); } #endif // MyPaintView.cpp: CMyPaintView 类的实现 // #include "pch.h" #include "framework.h" // SHARED_HANDLERS 可以在实现预览、缩略图和搜索筛选器句柄的 // ATL 项目中进行定义,并允许与该项目共享文档代码。 #ifndef SHARED_HANDLERS #include "MyPaint.h" #endif #include "MyPaintDoc.h" #include "MyPaintView.h" #include "resource.h" #include "CSetPenSizeDialog.h" #ifdef _DEBUG #define new DEBUG_NEW #endif // CMyPaintView IMPLEMENT_DYNCREATE(CMyPaintView, CView) BEGIN_MESSAGE_MAP(CMyPaintView, CView) // 标准打印命令 ON_COMMAND(ID_FILE_PRINT, &CView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_DIRECT, &CView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_PREVIEW, &CView::OnFilePrintPreview) ON_WM_LBUTTONDOWN() ON_WM_MOUSEMOVE() ON_WM_LBUTTONUP() ON_COMMAND(1, &CMyPaintView::OnDrawLineSegment) ON_COMMAND(2, &CMyPaintView::OnDrawRectangle) ON_COMMAND(3, &CMyPaintView::OnDrawCircle) ON_COMMAND(4, &CMyPaintView::OnDrawEllips) ON_COMMAND(5, &CMyPaintView::OnSetPencil) ON_COMMAND(6, &CMyPaintView::OnSetEraser) ON_COMMAND(7, &CMyPaintView::OnSetPenSize) ON_COMMAND(8, &CMyPaintView::OnSetColor) ON_COMMAND(9, &CMyPaintView::OnFileSave)
END_MESSAGE_MAP() // CMyPaintView 构造/析构 CMyPaintView::CMyPaintView() noexcept { // TODO: 在此处添加构造代码 m_PenSize = 1; m_PenColor = RGB(0, 0, 0), m_BrushColor = RGB(0, 0, 0); m_PointBegin = CPoint(0, 0); m_PointEnd = CPoint(0, 0); m_DrawType = DrawType::LineSetgment; } CMyPaintView::~CMyPaintView() { } BOOL CMyPaintView::PreCreateWindow(CREATESTRUCT& cs) { // TODO: 在此处通过修改 // CREATESTRUCT cs 来修改窗口类或样式 return CView::PreCreateWindow(cs); } // CMyPaintView 绘图 void CMyPaintView::OnDraw(CDC* /*pDC*/) { CMyPaintDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); if (!pDoc) return; // TODO: 在此处为本机数据添加绘制代码 } // CMyPaintView 打印 BOOL CMyPaintView::OnPreparePrinting(CPrintInfo* pInfo) { // 默认准备 return DoPreparePrinting(pInfo); } void CMyPaintView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/) { // TODO: 添加额外的打印前进行的初始化过程 } void CMyPaintView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/) { // TODO: 添加打印后进行的清理过程 } // CMyPaintView 诊断 #ifdef _DEBUG void CMyPaintView::AssertValid() const { CView::AssertValid(); } void CMyPaintView::Dump(CDumpContext& dc) const { CView::Dump(dc); } CMyPaintDoc* CMyPaintView::GetDocument() const // 非调试版本是内联的 { ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CMyPaintDoc))); return (CMyPaintDoc*)m_pDocument; } #endif //_DEBUG // CMyPaintView 消息处理程序 void CMyPaintView::OnLButtonDown(UINT nFlags, CPoint point) { // TODO: 在此添加消息处理程序代码和/或调用默认值 m_PointBegin = m_PointEnd = point; /*CClientDC dc(this); dc.MoveTo(m_PointBegin); dc.LineTo(m_PointEnd); */ CView::OnLButtonDown(nFlags, point); CView::OnLButtonDown(nFlags, point); } void CMyPaintView::OnMouseMove(UINT nFlags, CPoint point) { // TODO: 在此添加消息处理程序代码和/或调用默认值 if (nFlags & MK_LBUTTON) { //定义新笔和旧笔的指针 CClientDC dc(this); CPen newPen, * oldPen; newPen.CreatePen(PS_SOLID, m_PenSize, m_PenColor); oldPen = dc.SelectObject(&newPen); switch (m_DrawType) { case DrawType::LineSetgment: { dc.SetROP2(R2_NOTXORPEN); dc.MoveTo(m_PointBegin); dc.LineTo(m_PointEnd); dc.MoveTo(m_PointBegin); dc.LineTo(point); m_PointEnd = point; break; } case DrawType::Rectangle: {dc.SetROP2(R2_NOTXORPEN); dc.SelectStockObject(5); CRect rectP1(m_PointBegin, m_PointEnd); dc.Rectangle(rectP1); CRect rectP2(m_PointBegin, point); dc.Rectangle(rectP2); m_PointEnd = point; break; } case DrawType::Pencil: { m_PointBegin = m_PointEnd; m_PointEnd = point; dc.MoveTo(m_PointBegin); dc.LineTo(m_PointEnd); break; } case DrawType::Eraser: { COLORREF pColor = dc.GetBkColor(); CPen newPen(PS_SOLID, m_PenSize, pColor); dc.SelectObject(&newPen); m_PointBegin = m_PointEnd; m_PointEnd = point; dc.MoveTo(m_PointBegin); dc.LineTo(m_PointEnd); break; } case DrawType::Ellips: {dc.SetROP2(R2_NOTXORPEN); dc.SelectStockObject(5); CRect rectP1(m_PointBegin, m_PointEnd); dc.Ellipse(rectP1); CRect rectP2(m_PointBegin, point); dc.Ellipse(rectP2); m_PointEnd = point; break; } case DrawType::Circle: { dc.SetROP2(R2_NOTXORPEN); dc.SelectStockObject(5); int length_1 = m_PointEnd.y - m_PointBegin.y; if (m_PointEnd.x < m_PointBegin.x) { m_PointEnd.x = m_PointBegin.x - abs(length_1); } else { m_PointEnd.x = m_PointBegin.x + abs(length_1); } CRect rectP1(m_PointBegin, m_PointEnd); dc.Ellipse(rectP1); int length_2 = point.y - m_PointBegin.y; if (point.x < m_PointBegin.x) { m_PointEnd.x = m_PointBegin.x - abs(length_2); } else { m_PointEnd.x = m_PointBegin.x + abs(length_2); } m_PointEnd.y = point.y; CRect rectP2(m_PointBegin, m_PointEnd); dc.Ellipse(rectP2); m_PointEnd = point; break; } default: break; } dc.SelectObject(oldPen); } CView::OnMouseMove(nFlags, point); } void CMyPaintView::OnLButtonUp(UINT nFlags, CPoint point) { // TODO: 在此添加消息处理程序代码和/或调用默认值 CClientDC dc(this); CPen newPen, * oldPen; newPen.CreatePen(PS_SOLID, m_PenSize, m_PenColor); oldPen = dc.SelectObject(&newPen); switch (m_DrawType) { case DrawType::LineSetgment: { dc.MoveTo(m_PointBegin); dc.LineTo(point); break; } case DrawType::Rectangle: { dc.SelectStockObject(PS_NULL); CRect rectP2(m_PointBegin, point); dc.Rectangle(rectP2); break; } case DrawType::Pencil: { m_PointBegin = m_PointEnd; m_PointEnd = point; dc.MoveTo(m_PointBegin); dc.LineTo(m_PointEnd); break; } case DrawType::Ellips: { dc.SelectStockObject(PS_NULL); CRect rectP2(m_PointBegin, point); dc.Ellipse(rectP2); break; } default: break; } dc.SelectObject(oldPen); CView::OnLButtonUp(nFlags, point); } void CMyPaintView::OnDrawLineSegment() { m_DrawType = DrawType::LineSetgment; // TODO: 在此添加命令处理程序代码 } void CMyPaintView::OnDrawRectangle() { // TODO: 在此添加命令处理程序代码 m_DrawType = DrawType::Rectangle; } void CMyPaintView::OnDrawCircle() { // TODO: 在此添加命令处理程序代码 m_DrawType = DrawType::Circle; } void CMyPaintView::OnDrawEllips() { // TODO: 在此添加命令处理程序代码 m_DrawType = DrawType::Ellips; } void CMyPaintView::OnSetColor() { // TODO: 在此添加命令处理程序代码 CColorDialog dlg; if (IDOK == dlg.DoModal()) { m_PenColor = dlg.GetColor(); } } void CMyPaintView::OnSetPenSize() { // TODO: 在此添加命令处理程序代码 CSetPenSizeDialog dlg; if (IDOK == dlg.DoModal()) { this->m_PenSize = dlg.m_PenSize2; } } void CMyPaintView::OnSetPencil() { // TODO: 在此添加命令处理程序代码 m_DrawType = DrawType::Pencil; } void CMyPaintView::OnSetEraser() { // TODO: 在此添加命令处理程序代码 m_DrawType = DrawType::Eraser; } //添加事件处理程序,生成函数OnFileSave() : void CMyPaintView::OnFileSave() { CClientDC dc(this); CRect rect; BOOL showMsgTag; //是否要弹出”图像保存成功对话框" BOOL saveTag; CString saveFilePath; GetClientRect(&rect); //获取画布大小 HBITMAP hbitmap = CreateCompatibleBitmap(dc, rect.right - rect.left, rect.bottom - rect.top); //创建兼容位图 HDC hdc = CreateCompatibleDC(dc); //创建兼容DC,以便将图像保存为不同的格式 HBITMAP hOldMap = (HBITMAP)SelectObject(hdc, hbitmap); //将位图选入DC,并保存返回值 BitBlt(hdc, 0, 0, rect.right - rect.left, rect.bottom - rect.top, dc, 0, 0, SRCCOPY); //将屏幕DC的图像复制到内存DC中 CImage image; image.Attach(hbitmap); //将位图转化为一般图像 saveTag = FALSE; if (!saveTag) //如果图像是第一次被写入,则打开对话框 { showMsgTag = TRUE; CString strFilter = _T("位图文件|*.bmp|JPEG 图像文件|*.jpg|GIF图像文件 | *.gif | PNG图像文件 | *.png |其他格式| *.* |"); CFileDialog dlg(FALSE, _T("bmp"), _T("iPaint1.bmp"), NULL, strFilter); if (dlg.DoModal() != IDOK) return; CString strFileName; //如果用户没有指定文件扩展名,则为其添加一个 CString strExtension; strFileName = dlg.m_ofn.lpstrFile; if (dlg.m_ofn.nFileExtension = 0) //扩展名项目为0 { switch (dlg.m_ofn.nFilterIndex) { case 1: strExtension = "bmp"; break; case 2: strExtension = "jpg"; break; case 3: strExtension = "gif"; break; case 4: strExtension = "png"; break; default: break; } strFileName = strFileName + "." + strExtension; } saveFilePath = strFileName; //saveFilePath为视类中的全局变量,类型为CString } else { showMsgTag = FALSE; } //AfxMessageBox(saveFilePath); //显示图像保存的全路径(包含文件名) HRESULT hResult = image.Save(saveFilePath); //保存图像 if (FAILED(hResult)) { MessageBox(_T("保存图像文件失败!")); } else { if (showMsgTag) MessageBox(_T("文件保存成功!")); } image.Detach(); SelectObject(hdc, hOldMap); }
实现功能如下: (1)可以选择绘制多种图 (2)可以擦除所绘内容 (3)能够用选择不同颜色绘制图案 (4)能够调整画笔的粗细 (6)可以存储所绘制的图形
五、实验结果及分析 个人小结: 这个项目是一个基于MFC多文档的桌面应用程序,完成这个项目除了增加了对C++面向对象编程的理解,同时也加深了对MFC的理解,在整个过程中我侧重于把握前者。我的C++知识不怎么完善,写完整个项目的感觉像搭积木,而且对类中一些限定词(如const、override、virtual、protected),指针引用和STL中的容器对象不怎么理解,所以这一次重在把握程序设计的整体架构,加深理解面向对象继承和成员函数重载等概念,同时加深了对MFC的了解。 | ||||||||