程序设计课程设计报告
专 业: 计算机科学
班 级: 计科1024
学 号: 321000000000
姓 名: 老王666
计算机科学与网络工程学院
二零五零年六月
一、课程设计题目及内容
题目:绘图板
内容:
(1)可以用不同的线宽和颜色画线;
(2)可以修改所绘制图形的线宽和颜色;
(3)可以存储所绘制的图形(用MFC的CArchive类和文档视图中的序列化操作实现)。
(4)可以用鼠标拖动所绘制的图形。
二、功能模块的设计分析及算法描述
1、实现绘图数据的保存功能
在设计绘图过程中,我抽象出来一个“A”的笔画,它是由“/”、“\”以及“-”组成的。而每一笔画又是由一个个的点构成的。即一个数组,数组里面包含笔画,而笔画又是一个数组,数组里面包含着点。
于是,我创建了一个vector,叫做mScribble,里面存放着指向vector(vector里面装有点CPoint)的指针,即存放vector的地址。vector是一个能够存放任意类型的动态数组,能够增加和压缩数据。存放vector地址的好处就在于方便存取数据。
这样子就可以把绘图的折线段数据保存到vector动态数组里了。
2、实现绘图功能
绘图目标:实现鼠标左键按下并且鼠标移动时,开始绘图;而鼠标左键松开时不论鼠标移动与否都无法实现绘图。
为实现此功能,首先要创建一个画笔,定义画笔缺省的颜色以及缺省的线宽,然后需要通过类向导为鼠标左键按下、鼠标左键松开、鼠标左键移动添加处理程序。
定义一个指针,叫做currentScribble,当鼠标左键按下时,currentScribble指向绘图类指针mScribble。当鼠标左键松开时,将currentScribble指针指向空。当鼠标移动时,判断将currentScribble指针是否指向空,即鼠标左键是否按下,若鼠标左键按下,则随着鼠标的移动给容器vector增加点并不断进行重绘。
这样子就实现了绘图功能。
3、实现不同的线宽和颜色进行绘图
为实现改变线宽与颜色,可以增设三个工具条按钮,分别用来实现增加线宽、减少线宽和改变画线颜色。为工具条按钮添加事件处理程序,在处理程序中实现点击增加线宽按钮就可以增加线宽1的单位,点击减少线宽按钮就可以实现减少线宽1个单位。点击画线颜色按钮就可以弹出颜色对话框提供所需改变成的颜色,点击颜色对话框的确定键就可以实现改变画线颜色。
值得注意的是,我们需要限制线宽的大小。在实际开发中,我的线宽上限设置为50,下限设置为1。
除此之外,还要设计一个新涂鸦的工具条按钮,用来实现新的线宽与颜色的绘画。否则新涂鸦的线宽与颜色将保持原来的不变。
4、实现存储所绘图形
MFC中已经存在储存所绘图形的功能,但我们还是需要重写这个储存虚函数Serialize,才能实现储存功能。在虚函数Serialize中,判断IsStoring是否为真,为真时,即我们点击了保存按钮后,函数执行储存绘图语句,否则执行绘图语句。
5、实现所绘图形线宽和颜色的修改
为实现此功能,首先要做一个修改属性对话框,在对话框中增加两个控件:修改线宽控件、修改颜色控件,再分别增加控件的事件处理程序,具体实现输入线宽大小来修改线宽以及选择画笔颜色来修改画笔颜色。最后再增设了一个单击鼠标右键消息事件,并给该事件添加处理程序,具体实现弹出对话框从而改变所绘图形线宽、颜色的功能。
值得注意的是,我们需要设置点击鼠标右键弹出对话框的条件:鼠标位置与所绘图形位置足够近。
6、实现用鼠标拖动所绘制的图形。
为实现此功能,首先要增设一个工具条按钮,用来实现鼠标拖动图形的指令。为工具条按钮添加事件处理程序,在处理程序中实现点击移动所绘图形按钮后,mIsDraw=0,鼠标左键按下后并且移动鼠标时执行移动的语句块。再次点击移动所绘图形按钮后,mIsDraw=1,标左键按下后并且移动鼠标时执行绘图语句块。
三、程序中使用的数据及主要符号说明
折线段数据:
vector<vector<CPoint>*> mScribble;创建一个vector,叫做mScribble,里面存放着指向vector(vector里面装有点CPoint)的指针,即存放vector的地址。
能够存放任意类型的动态数组,能够增加和压缩数据:vector
点:CPoint
线宽:默认线宽为2
int mLineWidth = 2;
画笔颜色:默认画笔颜色为红色
COLORREF mColor = RGB(0xFF, 0x00, 0x00);
设置线宽与获取线宽的函数:
int GetLineWidth()const { return mLineWidth; }
void SetLineWidth(int w) { mLineWidth = w; }
设置画笔颜色与获取画笔颜色的函数:
COLORREF GetColor()const { return mColor;}
void SetColor(COLORREF color) { mColor = color; }
储存的虚函数:
void Serialize(CArchive& ar) override;
画图的函数:
void Draw(CDC* pDC);
增加分支的函数:
void AddBranch();
void AddPoint(const CPoint& pt, int branchIndex = -1);
选择(判断鼠标的点是否在绘图上)的函数:
bool At(CPoint pt);
移动(计算涂鸦移动位置的改变量)的函数:
void Offset(CSize& off);
把每一个折线段数据都取一遍,逐个删除:
for (auto s : mScribble)delete s;
创建一个新的画笔:
pen.CreatePen(PS_SOLID, mLineWidth, mColor);
将新画笔选到设备里面去,将旧的画笔替换成新的画笔:
auto oldPen = pDC->SelectObject(&pen);
用当前画笔描绘一系列线段 :
Polyline:
将旧画笔选到设备里面去:
pDC->SelectObject(oldPen);
在vector尾部加入一个数据:push_back
把每一个涂鸦都取一遍:
for (auto s : mScribble)
把每一个笔画都取一遍:
for (auto p : *s)
判断是否存在一个点使得x、y的距离都小于规定的值(我们称之为点在绘图上):
auto v = pt - p;
if (abs(v.cx) < 8 && abs(v.cy) < 8)return true;
两点间的距离:
CSize& off
创建一个容器mScribbles,容器里面装的是Scribble类指针:
vector<Scribble*> mScribbles;
Scribble类的指针,名为currentScribble,指向空:
Scribble* currentScribble = nullptr;
是否正在画图:
bool mIsDraw=1;
移动前的点的坐标:
CPoint mPrevPosition;
函数GetDocument()用于获取当前文档对象的指针:
auto pDoc = GetDocument();
增加新的点:
currentScribble->AddPoint(point);
重绘:
InvalidateRect(nullptr, false);
声明一个颜色对话框,对话框缺省的颜色为原先的颜色mColor:
CColorDialog dlg(mColor);
对话框弹出后程序停止在这一步,直到点了对话框的确定键dlg.DoModal()==IDOK,才会更改画笔颜色:
if (dlg.DoModal() == IDOK)mColor = dlg.GetColor();
左边标记的宏:
DECLARE_SERIAL(Scribble);
储存虚函数:
void Serialize(CArchive& ar) override;
改变所绘图形线宽与颜色的对话框:
CScribblePropertyDlg dlg
四、部分关键程序的源代码
1、Scribble.h
#pragma once
#include <afx.h>
#include<vector>
using namespace std;
class Scribble : public CObject
{
protected:
DECLARE_SERIAL(Scribble); //左标记的宏
public:
//构造函数
Scribble(int width = 2, COLORREF color = RGB(0x00, 0x00, 0xFF));
//析构函数
~Scribble();
//设置线宽与获取线宽的函数
int GetLineWidth()const { return mLineWidth; }
void SetLineWidth(int w) { mLineWidth = w; }
//设置画笔颜色与获取画笔颜色的函数
COLORREF GetColor()const { return mColor;}
void SetColor(COLORREF color) { mColor = color; }
//储存
void Serialize(CArchive& ar) override; //虚函数
//画图
void Draw(CDC* pDC);
//增加一个分支
void AddBranch();
void AddPoint(const CPoint& pt, int branchIndex = -1);
//选择(判断鼠标的点是否在绘图上)的函数
bool At(CPoint pt);
//移动(计算涂鸦移动位置的改变量)的函数
void Offset(CSize& off);
private:
//折线段数据
//创建一个vector, 叫做mScribble, 里面存放着指向vector(vector里面有点)的指针,即存放vector的地址
vector<vector<CPoint>*> mScribble; //vector是一个能够存放任意类型的动态数组,能够增加和压缩数据。CPoint为一个点
//线宽
int mLineWidth = 2;
//画笔颜色
COLORREF mColor = RGB(0xFF, 0x00, 0x00);
};
2、Scribble.cpp
include "pch.h"
#include "Scribble.h"
IMPLEMENT_SERIAL(Scribble, CObject, 0);
Scribble::Scribble(int width, COLORREF color) //构造函数:一个线宽为2、颜色为红色的默认画笔
:mLineWidth(width), mColor(color)
{
}
Scribble::~Scribble() //析构函数:把每一个折线段数据都取一遍,逐个删除。
{
for (auto s : mScribble)delete s;
}
//画图
void Scribble::Draw(CDC* pDC)
{
CPen pen;
pen.CreatePen(PS_SOLID, mLineWidth, mColor); //创建一个新的画笔,
auto oldPen = pDC->SelectObject(&pen); //将新画笔选到设备里面去,将旧的画笔替换成新的画笔,
for (auto s : mScribble)pDC->Polyline(s->data(), s->size()); //Polyline:用当前画笔描绘一系列线段
pDC->SelectObject(oldPen); //将旧画笔选到设备里面去
}
//增加一个分支
void Scribble::AddBranch()
{
mScribble.push_back(new vector<CPoint>()); //push_back在vector类中作用为在vector尾部加入一个数据
}
//增加点
void Scribble::AddPoint(const CPoint& pt, int branchIndex)
{
//若容器的大小为零,则在该容器尾部加入一个新的容器(容器里面有点)
if (mScribble.size() == 0)mScribble.push_back(new vector<CPoint>());
if (branchIndex >= 0 && branchIndex < (int)mScribble.size())mScribble[branchIndex]->push_back(pt);
else mScribble[mScribble.size() - 1]->push_back(pt);
//这是一个未实现的功能(实现在原来的笔画上增添新的点),只是留了一个接口
}
//选择 :判断选中的点是否在涂鸦附近
bool Scribble::At(CPoint pt)
{
for (auto s : mScribble)
{
for (auto p : *s) //把每个笔画都取一遍
{
auto v = pt - p;
if (abs(v.cx) < 8 && abs(v.cy) < 8)return true;
} //把一个容器里面的点都取一遍,判断是否存在一个点使得x、y的距离都小于规定的值(我们称之为点在绘图上),
}
return false;
}
//储存
void Scribble::Serialize(CArchive& ar)
{
CObject::Serialize(ar);
if (ar.IsStoring())
{
ar << mLineWidth;
ar << mColor;
ar << mScribble.size();
for (auto s : mScribble)
{
ar << s->size();
for (auto pt : *s) ar << pt;
}
}
else
{
ar >> mLineWidth;
ar >> mColor;
int n = 0;
ar >> n;
for (int i = 0; i < n; ++i)
{
vector<CPoint>* polyline = new vector<CPoint>();
int m = 0;
ar >> m;
for (int j = 0; j < m; j++)
{
CPoint pt;
ar >> pt;
polyline->push_back(pt);
}
mScribble.push_back(polyline);
}
}
}
//移动
void Scribble::Offset(CSize& off)
{
for (auto s : mScribble)
{
for (auto& pt : *s)
{
pt += off;
}
}
}
3、DrawScribbleDoc.h
// DrawScribbleDoc.h: CDrawScribbleDoc 类的接口
//
#pragma once
#include<vector>
class Scribble;
class CDrawScribbleDoc : public CDocument
{
protected: // 仅从序列化创建
CDrawScribbleDoc() noexcept;
DECLARE_DYNCREATE(CDrawScribbleDoc)
// 特性
public:
//线宽
int mWidth = 2;
//画线颜色
COLORREF mColor = RGB(0xFF, 0x00, 0x00);
//涂鸦数据
std::vector<Scribble*> mScribbles; //创建一个容器mScribbles,容器里面装的是Scribble类指针
// 操作
public:
void Draw(CDC* pDC);
// 重写
public:
virtual BOOL OnNewDocument();
virtual void Serialize(CArchive& ar);
#ifdef SHARED_HANDLERS
virtual void InitializeSearchContent();
virtual void OnDrawThumbnail(CDC& dc, LPRECT lprcBounds);
#endif // SHARED_HANDLERS
// 实现
public:
virtual ~CDrawScribbleDoc();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif
protected:
// 生成的消息映射函数
protected:
DECLARE_MESSAGE_MAP()
#ifdef SHARED_HANDLERS
// 用于为搜索处理程序设置搜索内容的 Helper 函数
void SetSearchContent(const CString& value);
#endif // SHARED_HANDLERS
public:
afx_msg void OnLineColor();
afx_msg void OnLineWidthAdd();
afx_msg void OnUpdateLineWidthAdd(CCmdUI* pCmdUI);
afx_msg void OnLineWidthSub();
afx_msg void OnUpdateLineWidthSub(CCmdUI* pCmdUI);
afx_msg void OnMoveScribble();
};
4、DrawScribbleDoc.cpp
// DrawScribbleDoc.cpp: CDrawScribbleDoc 类的实现
//
#include "pch.h"
#include "framework.h"
// SHARED_HANDLERS 可以在实现预览、缩略图和搜索筛选器句柄的
// ATL 项目中进行定义,并允许与该项目共享文档代码。
#ifndef SHARED_HANDLERS
#include "DrawScribble.h"
#endif
#include "DrawScribbleDoc.h"
#include "Scribble.h"
#include <propkey.h>
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// CDrawScribbleDoc
IMPLEMENT_DYNCREATE(CDrawScribbleDoc, CDocument)
BEGIN_MESSAGE_MAP(CDrawScribbleDoc, CDocument)
ON_COMMAND(ID_LINE_COLOR, &CDrawScribbleDoc::OnLineColor)
ON_COMMAND(ID_LINE_WIDTH_ADD, &CDrawScribbleDoc::OnLineWidthAdd)
ON_UPDATE_COMMAND_UI(ID_LINE_WIDTH_ADD, &CDrawScribbleDoc::OnUpdateLineWidthAdd)
ON_COMMAND(ID_LINE_WIDTH_SUB, &CDrawScribbleDoc::OnLineWidthSub)
ON_UPDATE_COMMAND_UI(ID_LINE_WIDTH_SUB, &CDrawScribbleDoc::OnUpdateLineWidthSub)
ON_COMMAND(ID_MOVE_SCRIBBLE, &CDrawScribbleDoc::OnMoveScribble)
END_MESSAGE_MAP()
// CDrawScribbleDoc 构造/析构
CDrawScribbleDoc::CDrawScribbleDoc() noexcept
{
// TODO: 在此添加一次性构造代码
}
CDrawScribbleDoc::~CDrawScribbleDoc()
{
for (auto s : mScribbles) delete s;
}
BOOL CDrawScribbleDoc::OnNewDocument()
{
if (!CDocument::OnNewDocument())
return FALSE;
// TODO: 在此添加重新初始化代码
// (SDI 文档将重用该文档)
return TRUE;
}
// CDrawScribbleDoc 序列化
//储存
void CDrawScribbleDoc::Serialize(CArchive& ar)
{
if (ar.IsStoring())
{
// TODO: 在此添加存储代码
ar << mScribbles.size();
for (auto s : mScribbles) s->Serialize(ar);
}
else
{
// TODO: 在此添加加载代码
int n = 0;
ar >> n;
for (int k = 0; k < n; ++k)
{
Scribble* s = new Scribble();
s->Serialize(ar);
mScribbles.push_back(s);
}
}
}
#ifdef SHARED_HANDLERS
// 缩略图的支持
void CDrawScribbleDoc::OnDrawThumbnail(CDC& dc, LPRECT lprcBounds)
{
// 修改此代码以绘制文档数据
dc.FillSolidRect(lprcBounds, RGB(255, 255, 255));
CString strText = _T("TODO: implement thumbnail drawing here");
LOGFONT lf;
CFont* pDefaultGUIFont = CFont::FromHandle((HFONT) GetStockObject(DEFAULT_GUI_FONT));
pDefaultGUIFont->GetLogFont(&lf);
lf.lfHeight = 36;
CFont fontDraw;
fontDraw.CreateFontIndirect(&lf);
CFont* pOldFont = dc.SelectObject(&fontDraw);
dc.DrawText(strText, lprcBounds, DT_CENTER | DT_WORDBREAK);
dc.SelectObject(pOldFont);
}
// 搜索处理程序的支持
void CDrawScribbleDoc::InitializeSearchContent()
{
CString strSearchContent;
// 从文档数据设置搜索内容。
// 内容部分应由“;”分隔
// 例如: strSearchContent = _T("point;rectangle;circle;ole object;");
SetSearchContent(strSearchContent);
}
void CDrawScribbleDoc::SetSearchContent(const CString& value)
{
if (value.IsEmpty())
{
RemoveChunk(PKEY_Search_Contents.fmtid, PKEY_Search_Contents.pid);
}
else
{
CMFCFilterChunkValueImpl *pChunk = nullptr;
ATLTRY(pChunk = new CMFCFilterChunkValueImpl);
if (pChunk != nullptr)
{
pChunk->SetTextValue(PKEY_Search_Contents, value, CHUNK_TEXT);
SetChunkValue(pChunk);
}
}
}
#endif // SHARED_HANDLERS
// CDrawScribbleDoc 诊断
#ifdef _DEBUG
void CDrawScribbleDoc::AssertValid() const
{
CDocument::AssertValid();
}
void CDrawScribbleDoc::Dump(CDumpContext& dc) const
{
CDocument::Dump(dc);
}
#endif //_DEBUG
// CDrawScribbleDoc 命令
void CDrawScribbleDoc::Draw(CDC* pDC)
{
for (auto s : mScribbles) s->Draw(pDC);
}
void CDrawScribbleDoc::OnLineColor()
{
// TODO: 在此添加命令处理程序代码
CColorDialog dlg(mColor); //声明一个颜色对话框,对话框缺省的颜色为原先的颜色mColor
//对话框弹出后程序停止在这一步,直到点了对话框的确定键,即dlg.DoModal()==IDOK,才会更改画笔颜色
if (dlg.DoModal() == IDOK)mColor = dlg.GetColor();
}
void CDrawScribbleDoc::OnLineWidthAdd()
{
// TODO: 在此添加命令处理程序代码
mWidth += 1; //线宽加一
}
void CDrawScribbleDoc::OnUpdateLineWidthAdd(CCmdUI* pCmdUI)
{
// TODO: 在此添加命令更新用户界面处理程序代码
pCmdUI->Enable(mWidth < 50); //当线宽小于50时,允许增加线宽。否则不能
}
void CDrawScribbleDoc::OnLineWidthSub()
{
// TODO: 在此添加命令处理程序代码
mWidth -= 1; //线宽减一
}
void CDrawScribbleDoc::OnUpdateLineWidthSub(CCmdUI* pCmdUI)
{
// TODO: 在此添加命令更新用户界面处理程序代码
pCmdUI->Enable(mWidth > 1); //当线宽大于1时,允许减小线宽。否则不能
}
void CDrawScribbleDoc::OnMoveScribble()
{
// TODO: 在此添加命令处理程序代码
}
5、DrawScribbleView.h
// DrawScribbleView.h: CDrawScribbleView 类的接口
//
#pragma once
class Scribble;
class CDrawScribbleView : public CView
{
protected: // 仅从序列化创建
CDrawScribbleView() noexcept;
DECLARE_DYNCREATE(CDrawScribbleView)
// 特性
public:
CDrawScribbleDoc* GetDocument() const;
// 操作
public:
Scribble* currentScribble = nullptr; //Scribble类的指针,名为currentScribble,指向空
// 重写
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 ~CDrawScribbleView();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif
protected:
// 生成的消息映射函数
protected:
afx_msg void OnFilePrintPreview();
afx_msg void OnRButtonUp(UINT nFlags, CPoint point);
afx_msg void OnContextMenu(CWnd* pWnd, CPoint point);
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
afx_msg void OnNewScribble();
afx_msg void OnRButtonDown(UINT nFlags, CPoint point);
private:
bool mIsDraw=1;
CPoint mPrevPosition;
public:
afx_msg void OnMoveScribble();
};
#ifndef _DEBUG // DrawScribbleView.cpp 中的调试版本
inline CDrawScribbleDoc* CDrawScribbleView::GetDocument() const
{ return reinterpret_cast<CDrawScribbleDoc*>(m_pDocument); }
#endif
6、DrawScribbleView.cpp
// DrawScribbleView.cpp: CDrawScribbleView 类的实现
//
#include "pch.h"
#include "framework.h"
// SHARED_HANDLERS 可以在实现预览、缩略图和搜索筛选器句柄的
// ATL 项目中进行定义,并允许与该项目共享文档代码。
#ifndef SHARED_HANDLERS
#include "DrawScribble.h"
#endif
#include "DrawScribbleDoc.h"
#include "DrawScribbleView.h"
#include "Scribble.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
#include "CScribblePropertyDlg.h"
// CDrawScribbleView
IMPLEMENT_DYNCREATE(CDrawScribbleView, CView)
BEGIN_MESSAGE_MAP(CDrawScribbleView, CView)
// 标准打印命令
ON_COMMAND(ID_FILE_PRINT, &CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT, &CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW, &CDrawScribbleView::OnFilePrintPreview)
ON_WM_CONTEXTMENU()
ON_WM_RBUTTONUP()
ON_WM_MOUSEMOVE()
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONUP()
ON_COMMAND(ID_NEW_SCRIBBLE, &CDrawScribbleView::OnNewScribble)
ON_WM_RBUTTONDOWN()
ON_COMMAND(ID_MOVE_SCRIBBLE, &CDrawScribbleView::OnMoveScribble)
END_MESSAGE_MAP()
// CDrawScribbleView 构造/析构
CDrawScribbleView::CDrawScribbleView() noexcept
{
// TODO: 在此处添加构造代码
}
CDrawScribbleView::~CDrawScribbleView()
{
}
BOOL CDrawScribbleView::PreCreateWindow(CREATESTRUCT& cs)
{
// TODO: 在此处通过修改
// CREATESTRUCT cs 来修改窗口类或样式
return CView::PreCreateWindow(cs);
}
// CDrawScribbleView 绘图
void CDrawScribbleView::OnDraw(CDC* pDC)
{
CDrawScribbleDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
// TODO: 在此处为本机数据添加绘制代码
pDoc->Draw(pDC);
}
// CDrawScribbleView 打印
void CDrawScribbleView::OnFilePrintPreview()
{
#ifndef SHARED_HANDLERS
AFXPrintPreview(this);
#endif
}
BOOL CDrawScribbleView::OnPreparePrinting(CPrintInfo* pInfo)
{
// 默认准备
return DoPreparePrinting(pInfo);
}
void CDrawScribbleView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
// TODO: 添加额外的打印前进行的初始化过程
}
void CDrawScribbleView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
// TODO: 添加打印后进行的清理过程
}
void CDrawScribbleView::OnRButtonUp(UINT /* nFlags */, CPoint point)
{
ClientToScreen(&point);
OnContextMenu(this, point);
}
void CDrawScribbleView::OnContextMenu(CWnd* /* pWnd */, CPoint point)
{
#ifndef SHARED_HANDLERS
theApp.GetContextMenuManager()->ShowPopupMenu(IDR_POPUP_EDIT, point.x, point.y, this, TRUE);
#endif
}
// CDrawScribbleView 诊断
#ifdef _DEBUG
void CDrawScribbleView::AssertValid() const
{
CView::AssertValid();
}
void CDrawScribbleView::Dump(CDumpContext& dc) const
{
CView::Dump(dc);
}
CDrawScribbleDoc* CDrawScribbleView::GetDocument() const // 非调试版本是内联的
{
ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CDrawScribbleDoc)));
return (CDrawScribbleDoc*)m_pDocument;
}
#endif //_DEBUG
// CDrawScribbleView 消息处理程序
//鼠标移动时执行:
void CDrawScribbleView::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
if ((nFlags & MK_LBUTTON) != 0)
{
auto pDoc = GetDocument();
if (mIsDraw)
{
currentScribble->AddPoint(point); //1、增加新的点
InvalidateRect(nullptr); //重绘
}
else
{
if (currentScribble != nullptr)
{
currentScribble->Offset(point - mPrevPosition); //移动的距离为:新的点的坐标与旧的点的坐标的差
mPrevPosition = point; //将当前的点赋值给先前的点,即新的点又重新变成旧的点
InvalidateRect(nullptr); //重绘
}
}
}
CView::OnMouseMove(nFlags, point);
}
//鼠标左键按下时执行:
void CDrawScribbleView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
auto pDoc = GetDocument(); //函数GetDocument()用于获取当前文档对象的指针
//若容器的大小为零,则在该容器尾部加入一个新的容器
if (mIsDraw)
{
if (pDoc->mScribbles.empty())
{
pDoc->mScribbles.push_back(new Scribble(pDoc->mWidth, pDoc->mColor));
}
currentScribble = pDoc->mScribbles.back(); //将容器mScribble尾部的内容即Scribble类指针赋值给pDoc
currentScribble->AddBranch();
currentScribble->AddPoint(point);
}
else
{
for (auto s : pDoc->mScribbles)
{
if (s->At(point)) //判断点是否在涂鸦上
{
currentScribble = s;
mPrevPosition = point; //将当前的点赋值给先前的点,即新的点又重新变成旧的点
break;
}
}
}
InvalidateRect(nullptr); //重绘
CView::OnLButtonDown(nFlags, point);
}
//鼠标左键松开时执行:
void CDrawScribbleView::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
currentScribble = nullptr; //将指针再次指向空
CView::OnLButtonUp(nFlags, point);
}
void CDrawScribbleView::OnNewScribble()
{
// TODO: 在此添加命令处理程序代码
auto pDoc = GetDocument();
pDoc->mScribbles.push_back(new Scribble(pDoc->mWidth, pDoc->mColor)); //增加一个新的涂鸦
}
void CDrawScribbleView::OnRButtonDown(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
auto pDoc = GetDocument();
for (auto s : pDoc->mScribbles) //遍历每一个涂鸦,判断这个点是否在涂鸦上,点在涂鸦上时,执行下面的语句
{
if (s->At(point))
{
CScribblePropertyDlg dlg;
dlg.mLineWidth = s->GetLineWidth();
dlg.mColor = s->GetColor();
if (dlg.DoModal() == IDOK) //点击确定键,改变选择的线宽及颜色
{
s->SetLineWidth(dlg.mLineWidth);
s->SetColor(dlg.mColor);
InvalidateRect(nullptr, true);
}
break;
}
}
CView::OnRButtonDown(nFlags, point);
}
void CDrawScribbleView::OnMoveScribble()
{
// TODO: 在此添加命令处理程序代码
mIsDraw = !mIsDraw; //点击移动按钮,mIsDraw取反,即绘图与移动可以通过点击此按钮进行切换
}
五、程序运行时的效果图
1、使用不同的线宽和颜色进行画线
效果如下图:
2、修改所绘制图形的线宽和颜色
效果如下图:
3、存储所绘制的图形(用MFC的CArchive类和文档视图中的序列化操作实现)
效果如下图:
4、用鼠标拖动所绘制的图形
六、实验结果分析,实验收获和体会
(1)实验结果分析
1、程序运行后,能够长按鼠标左键并且移动鼠标来进行绘图,绘出图形的默认颜色为红色,默认线宽为2。
2、点击增加线宽或点击减少线宽按钮后再点击新涂鸦按钮后,就可以画出一个改变线宽后的新涂鸦。
3、点击画线颜色按钮后再点击新涂鸦按钮,就可以画出一个指定颜色的新涂鸦。
4、在所绘图形的附近点击鼠标的右键,会弹出一个涂鸦属性对话框,该对话框具备输入线宽和选择颜色的功能,修改线宽和颜色后点击确定键就可以修改所绘制图形的线宽和颜色。
5、点击移动所绘图形按钮后,将鼠标点击所要移动的涂鸦并长按鼠标左键、移动鼠标,就可以将涂鸦移动。再次点击移动所绘图形按钮后,可以继续进行绘图。
6、点击保存按钮后,可以存储所绘制的图形到电脑的任意位置。将绘图板关闭后,点击打开现有文档按钮,选择之前保存的所绘图形的文件,点击打开,就可以打开原先所绘制的图形。
(2)实验收获与体会
因为课时少的缘故,我学习MFC的Windows程序设计的时间并不长,动手实践也不够多,但是通过这次的程序设计课程设计,我运用了C++面向对象的程序设计的知识,独立地完成了绘图板的设计。在设计过程中,我领会到了面向对象的编程思想与设计方法,体会到了亲手实现可视化编程的乐趣。遇到不懂的函数概念、函数使用方法以及设计思路,我通过网上搜索的方式以及课堂上询问老师的方式,一步一个脚印地进行逐层攻关、逐个突破,这大大提高了我分析问题和解决问题地能力,使我深入了解了系统开发的过程,熟悉了程序设计的方法。我还明白了View窗口与Doc窗口的使用规则,掌握了抽象化实际问题并进行代码实现的技术。
七、自评成绩
自评成绩:A+
原因:在这个课程设计的过程中,我花费了很多时间进行理解老师上课讲的代码的原理,并亲身实践。在这个过程中,我查阅了很多资料,遇到难理解的问题也向老师提问,最终独立完成了具有可修改画笔颜色和线宽、可移动所绘图形、可储存功能的绘图板。