MFC之学习交互式绘图技术、三角形交互式实时绘制

1.笔记

1.1回显技术

回显是对图形的操作,用某种方式表达出来的技术。例如,在窗口客户区使用鼠标移动顶点时,希望能实时显示顶点坐标,这就是一种回显技术。

1.2引力域技术

绘图过程中,常常需要使用鼠标光标选择某一顶点。要准确定位光标很难,这时可以采用引力域技术。

引力域是指以某一点为中心所建立的一个矩形区域,当光标处于矩形区域之内时,就被“引力”吸引到该点上来。需要注意的是引力域大小要选择适当,太小了没有引力,太大了容易出现错误连接。

1.3橡皮筋技术

橡皮筋技术是鼠标绘图的过程连续、动态地表现出来,直到产生用户满意的结果为止的技术。例如,使用鼠标移动多边形的一个顶点,随着鼠标的移动,多边形的形状在不断变化,就像由橡皮筋构成。橡皮筋技术一般需要双缓冲机制

双缓冲机制是通过增加一个内存缓冲区,在缓冲区将图画好再一次性拷贝到显示设备上,以解决频繁擦除屏幕所导致的屏幕闪烁问题。

2.使用练习

2.1使鼠标能够移动三角形的顶点,回显顶点坐标,用橡皮筋技术动态显示移动过程。

首先在工程的视图类中定义三角形顶点及相关变量。添加消息处理函数的方法之前已经学习过,故略。

// 在Example_1View.h文件中编辑

class CExample1View : public CView{
...
protected:
	CPoint p[3];//三角形三个顶点
	BOOL bLBDown;//左键按下状态
	int nCount;//顶点计数器
public:
	afx_msg void OnLButtonDown(UINT nFlags, CPoint point);//处理左键按下消息
	afx_msg void OnLButtonUp(UINT nFlags, CPoint point);//处理左键弹起消息
	void DoubleBuffer(CDC* pDC);//双缓冲函数
	void DrawTriangle(CDC* pDC);//绘制三角形
	afx_msg void OnMouseMove(UINT nFlags, CPoint point);//处理光标移动消息
};

然后给工程的视图类进行相应的初始化。

//在Example_1View.cpp文件中编辑

CExample1View::CExample1View() noexcept
{
	// TODO: 在此处添加构造代码
	p[0] = CPoint(100, 100);//三角形三个顶点初始化
	p[1] = CPoint(300, 200);
	p[2] = CPoint(50, 300);
	bLBDown = false;//左键按下状态初始化
	nCount = 0;//顶点计数器初始化
}

重写界面绘图函数。

//在Example_1View.cpp文件中编辑

void CExample1View::OnDraw(CDC* pDC)
{
	CExample1Doc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	if (!pDoc)
		return;

	// TODO: 在此处为本机数据添加绘制代码
	DoubleBuffer(pDC);
}

实现双缓冲函数。

//在Example_1View.cpp文件中编辑

void CExample1View::DoubleBuffer(CDC* pDC) {
	CRect rect;//获取客户区
	GetClientRect(&rect);

	CDC memDC;//缓冲区设备上下文要与显示设备上下文兼容
	memDC.CreateCompatibleDC(pDC);

	CBitmap newBitmap, * pOldBitmap;//准备好缓冲区
        //创建和显示设备上下文客户区一致的位图,用于绘图
	newBitmap.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height());
	pOldBitmap = memDC.SelectObject(&newBitmap);//将位图选入缓冲区
	memDC.FillSolidRect(rect, pDC->GetBkColor());//填充缓冲区背景

	DrawTriangle(&memDC);//画三角形,缓冲区绘制完后将图像拷贝回显示设备上下文
	pDC->BitBlt(rect.left, rect.top, rect.Width(), rect.Height(),
		&memDC, 0, 0, SRCCOPY);

	memDC.SelectObject(pOldBitmap);//恢复缓冲区设备上下文
	newBitmap.DeleteObject();//释放用毕资源
	memDC.DeleteDC();
}

实现绘制三角形函数。

//在Example_1View.cpp文件中编辑

void CExample1View::DrawTriangle(CDC* pDC) {
	pDC->SelectStockObject(GRAY_BRUSH);//选灰色画刷

	for (int i = 0; i < 3; ++i) {//3个顶点依次处理
		CString str;
		str.Format(CString("x=%d,y=%d"), p[i].x, p[i].y);
		pDC->SetTextColor(RGB(255, 0, 0));
		pDC->TextOutW(p[i].x, p[i].y, str);//输出顶点实时坐标
                
                //绘制顶点的引力域
		pDC->Rectangle(p[i].x - 5, p[i].y - 5, p[i].x + 5, p[i].y + 5);
                
                //连线,画三角形
		if (i == 0)pDC->MoveTo(p[i]);
		else pDC->LineTo(p[i]);
	}

	pDC->LineTo(p[0]);//连最后一条边
}

用鼠标拖动顶点的过程中,三角形的形状在不断变化,即要不断更新三角形的顶点信息,擦除画板重新绘制三角形。实现鼠标移动消息的处理函数。注意此处的GetDC和ReleaseDC配对出现是行规,否则容易出现内存泄漏问题。

//在Example_1View.cpp文件中编辑

void CExample1View::OnMouseMove(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	CDC* pDC = GetDC();//申请画图资源

	for(int i=0;i<3;++i)//寻找光标所在的顶点
		if (point.x<p[i].x + 5 && point.x > p[i].x - 5 &&
			point.y < p[i].y + 5 && point.y > p[i].y - 5)
		{
			//将光标形状设置为手形
                        SetCursor(LoadCursor(NULL, IDC_HAND));
			nCount = i;//标记光标所在的顶点
		}
	
	//如果鼠标左键按下,将光标当前位置的坐标赋值给顶点
	if (bLBDown)p[nCount] = point;

	ReleaseDC(pDC);//释放资源
	Invalidate(FALSE);//触发客户区重绘,不擦除背景直接画
        //Invalidate(TRUE)是擦除背景再画
        //因为是先在缓冲区绘制好图像,再一次性拷贝到显示设备上下文
        //所以显示设备上下文重绘客户区时不需要擦除背景,直接贴图即可

	CView::OnMouseMove(nFlags, point);
}

 鼠标坐标按下、抬起的消息处理则非常简单。

//在Example_1View.cpp文件中编辑

void CExample1View::OnLButtonDown(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	bLBDown = true;//鼠标左键按下,记为真

	CView::OnLButtonDown(nFlags, point);
}


void CExample1View::OnLButtonUp(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	bLBDown = false;//鼠标左键抬起,记为假

	CView::OnLButtonUp(nFlags, point);
}

该案例所有需要自行编写代码如上所示,鼠标移动消息处理函数OnMouseMove中申请绘图设备做何用,又为什么释放设备才去触发界面重绘事件,暂时还未明白,先记录之。

实现效果:

交互式实时绘制三角形

  • 3
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
银行家算法是一种用于避免死锁的算法,它通过检测系统资源的分配状态来判断是否能够满足进程的资源需求。下面给出一个 MFC C++ 实现的银行家算法交互式界面的示例代码。 1. 创建一个 MFC SDI 应用程序,并在菜单添加“计算”菜单。 2. 在“计算”菜单下添加“输入数据”和“执行算法”两个子菜单。 3. 在菜单栏类的头文件添加以下代码: ```c++ class CBankerAlgorithmMenu : public CMenu { public: CBankerAlgorithmMenu(void); virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct); virtual void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct); virtual BOOL GetMenuItemInfo(UINT uItem, LPMENUITEMINFO lpMenuItemInfo, BOOL bByPosition); }; ``` 4. 在菜单栏类的源文件添加以下代码: ```c++ CBankerAlgorithmMenu::CBankerAlgorithmMenu(void) { } void CBankerAlgorithmMenu::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) { // 绘制菜单项 } void CBankerAlgorithmMenu::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct) { // 计算菜单项的大小 } BOOL CBankerAlgorithmMenu::GetMenuItemInfo(UINT uItem, LPMENUITEMINFO lpMenuItemInfo, BOOL bByPosition) { // 获取菜单项信息 } ``` 5. 创建一个对话框类 CBankerAlgorithmDlg,并添加以下控件: - 一个静态文本框,用于显示输入数据和算法执行结果。 - 一个编辑框,用于输入进程数目。 - 一个编辑框,用于输入资源种类数目。 - 一个按钮,用于输入数据。 - 一个按钮,用于执行算法。 6. 在 CBankerAlgorithmDlg 类的头文件添加以下代码: ```c++ class CBankerAlgorithmDlg : public CDialogEx { public: CBankerAlgorithmDlg(CWnd* pParent = nullptr); enum { IDD = IDD_BANKERALGORITHM_DIALOG }; protected: virtual void DoDataExchange(CDataExchange* pDX); DECLARE_MESSAGE_MAP() private: CEdit m_editProcessCount; CEdit m_editResourceCount; CStatic m_staticData; }; ``` 7. 在 CBankerAlgorithmDlg 类的源文件添加以下代码: ```c++ CBankerAlgorithmDlg::CBankerAlgorithmDlg(CWnd* pParent /*=nullptr*/) : CDialogEx(IDD_BANKERALGORITHM_DIALOG, pParent) { // ... } void CBankerAlgorithmDlg::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); DDX_Control(pDX, IDC_EDIT_PROCESS_COUNT, m_editProcessCount); DDX_Control(pDX, IDC_EDIT_RESOURCE_COUNT, m_editResourceCount); DDX_Control(pDX, IDC_STATIC_DATA, m_staticData); } BEGIN_MESSAGE_MAP(CBankerAlgorithmDlg, CDialogEx) ON_BN_CLICKED(IDC_BUTTON_INPUT_DATA, &CBankerAlgorithmDlg::OnBnClickedButtonInputData) ON_BN_CLICKED(IDC_BUTTON_EXECUTE_ALGORITHM, &CBankerAlgorithmDlg::OnBnClickedButtonExecuteAlgorithm) END_MESSAGE_MAP() ``` 8. 在 CBankerAlgorithmDlg 类的源文件实现按钮的响应函数: ```c++ void CBankerAlgorithmDlg::OnBnClickedButtonInputData() { // 处理输入数据按钮的点击事件 } void CBankerAlgorithmDlg::OnBnClickedButtonExecuteAlgorithm() { // 处理执行算法按钮的点击事件 } ``` 9. 在 CBankerAlgorithmDlg 类的源文件实现输入数据和执行算法的函数: ```c++ void CBankerAlgorithmDlg::InputData() { CString strProcessCount, strResourceCount; m_editProcessCount.GetWindowText(strProcessCount); m_editResourceCount.GetWindowText(strResourceCount); int nProcessCount = _ttoi(strProcessCount); int nResourceCount = _ttoi(strResourceCount); // TODO: 处理输入数据 } void CBankerAlgorithmDlg::ExecuteAlgorithm() { // TODO: 处理执行算法 } ``` 10. 在 CBankerAlgorithmDlg 类的源文件添加以下代码,用于显示输入数据和算法执行结果: ```c++ void CBankerAlgorithmDlg::ShowData(LPCTSTR lpszData) { m_staticData.SetWindowText(lpszData); } ``` 11. 实现银行家算法,并在 ExecuteAlgorithm 函数调用: ```c++ void CBankerAlgorithmDlg::ExecuteAlgorithm() { // ... // 调用银行家算法 CString strData = BankerAlgorithm(nProcessCount, nResourceCount, Max, Allocation, Need, Available); ShowData(strData); } ``` 12. 编写银行家算法的函数: ```c++ CString CBankerAlgorithmDlg::BankerAlgorithm(int nProcessCount, int nResourceCount, int Max[MAX_PROCESS_COUNT][MAX_RESOURCE_COUNT], int Allocation[MAX_PROCESS_COUNT][MAX_RESOURCE_COUNT], int Need[MAX_PROCESS_COUNT][MAX_RESOURCE_COUNT], int Available[MAX_RESOURCE_COUNT]) { // 初始化 Finish 和 Work 数组 bool Finish[MAX_PROCESS_COUNT] = { false }; int Work[MAX_RESOURCE_COUNT]; for (int i = 0; i < nResourceCount; i++) Work[i] = Available[i]; // 查找满足条件的进程 int nCount = 0; int Sequence[MAX_PROCESS_COUNT]; while (nCount < nProcessCount) { bool bFound = false; for (int i = 0; i < nProcessCount; i++) { if (!Finish[i]) { bool bEnough = true; for (int j = 0; j < nResourceCount; j++) { if (Need[i][j] > Work[j]) { bEnough = false; break; } } if (bEnough) { for (int j = 0; j < nResourceCount; j++) Work[j] += Allocation[i][j]; Finish[i] = true; Sequence[nCount++] = i; bFound = true; } } } if (!bFound) break; } // 输出结果 CString strData; if (nCount < nProcessCount) { strData = _T("发生死锁!"); } else { strData = _T("安全序列为:"); for (int i = 0; i < nProcessCount; i++) strData.AppendFormat(_T("%d "), Sequence[i]); } return strData; } ``` 13. 在菜单栏类的源文件添加以下代码,用于处理菜单项的点击事件: ```c++ void CMainFrame::OnInputData() { // 创建对话框 CBankerAlgorithmDlg dlg(this); dlg.DoModal(); } void CMainFrame::OnExecuteAlgorithm() { // TODO: 执行算法 } ``` 14. 在菜单栏类的源文件添加以下代码,用于创建菜单栏和菜单项: ```c++ BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT /*lpcs*/, CCreateContext* pContext) { // ... // 创建菜单栏 if (!m_menuBankerAlgorithm.CreateMenu()) return FALSE; // 添加菜单项 m_menuBankerAlgorithm.AppendMenu(MF_STRING, ID_INPUT_DATA, _T("输入数据")); m_menuBankerAlgorithm.AppendMenu(MF_STRING, ID_EXECUTE_ALGORITHM, _T("执行算法")); SetMenu(&m_menuBankerAlgorithm); // ... return TRUE; } ``` 至此,一个 MFC C++ 银行家算法交互式界面的示例代码就完成了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

赴星辰大海

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值