深入浅出CChart 每日一课——快乐高四第四十九课 旧石器时代,老血狂喷之控制台窗口绘图

前面快乐高四第三十三课,介绍了在控制台程序中怎么利用CChart绘制曲线,那里的方法呢,很简单,就是创建一个弹出窗口,然后在这个弹出窗口上绘图,其实技术含量比较低。

这一课呢,笨笨想给大家介绍一下怎么直接在控制台窗口上绘图。什么,是那个黑不拉几的Dos窗口吗?听到这个想法,你是不是想吐血啊?哈哈!!!!!

在控制台窗口绘图,最困难的是Win32窗口中我们认为理所当然的东西,里面居然没有,比如各种消息!

下面还是开始吧,让我们回到旧石器时代。

第一步,建立一个控制台程序,注意选择“A simple application”,这样IDE会帮忙建立一个空的程序,省一点事。

 第二步,引入CChart,并加入要用的头文件。

#include "Chart.h"
#if defined(_UNICODE) || defined(UNICODE)
#	pragma comment(lib,"CChartu.lib")
#else
#	pragma comment(lib,"CChart.lib")
#endif
using namespace NsCChart;
 
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <math.h>
 
#define WM_MOUSEWHEEL 0x020A
 
extern "C" WINBASEAPI HWND WINAPI GetConsoleWindow();

这里引入了Windows的一个未公开的API,GetConsoleWindow(),用于找到程序的控制台窗口句柄。也可用FindWindow这个API,但比较麻烦。

第三步,添加两个全局变量。

CChart chart;
HWND hWnd;

这里chart用来绘图,不需多说,hWnd用于保存控制台窗口句柄。

第四步,编写绘图子函数。

// 绘图函数
void	MyDraw()
{
	HDC hDC = GetDC(hWnd);
	
	RECT rt;
	GetClientRect(hWnd, &rt);
	rt.top = (rt.top+rt.bottom)/2;
	chart.OnDraw(hDC, rt);
 
	ReleaseDC(hWnd, hDC);
}

这个套路大家应该都很熟悉,注意这里我们想利用控制台窗口的下半拉画图。

有了全局的HWND句柄,利用GetDC获得HDC,在HDC上绘图。最后必须释放HDC,不然内存会泄露。

第五步,在main()函数的一开始,就初始化hWnd。

hWnd = GetConsoleWindow();

这里就是利用了那个未公开的API获得控制台窗口句柄。

第六步,设置CChart数据。

    double pi = 4.0*atan(1.0);
	int perioid = 360;
	for(int i=0; i<4*perioid; ++i)
	{
		chart.AddPoint2D(i, 1.4*sin(i*2.0*pi/perioid));
	}
    chart.SetTitle(_T("在控制台窗口中绘图"));

当然这里随便怎么添加数据都可以的。

第七步,绘图。在上面的代码后面添加:

    MyDraw();
	printf("控制台窗口绘图!\n");

这里简单调用了前面编写的绘图子函数。由于我们是在窗口下半拉绘图,上半拉仍然可以用于控制台的字符输出,所以这里测试了一下printf输出函数。

效果如图呢!

怎么样?还是不错吧!

第八步,调整一下样式。

这里CChart默认是白背景,控制台窗口是黑背景,有点不协调。那我们添加下面两句话调整一下。

    chart.SetBkgndColor(RGB(0, 0, 0));
	chart.SetTitleColor(RGB(245, 245, 245));

现在的效果如图。

这下融为一体了。

第九步,添加消息循环,并增加重绘功能。

从输出图像可以看到Press any key to continue字样,老鸟都知道这表示程序已经结束。

另外我们如果把这个控制台窗口隐藏一下再打开,图像就消失了。这不符合我们的需要。

在上面的代码后面添加如下代码。

    HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE);
	HANDLE Hout = GetStdHandle(STD_OUTPUT_HANDLE);
	INPUT_RECORD inp;
	DWORD recnum;
	
	bool loop = true;
	while(loop)
	{
		ReadConsoleInput(hIn, &inp, 1, &recnum);
		switch(inp.EventType)
		{
		case FOCUS_EVENT:
			MyDraw();
			break;
		}
	}
	
	CloseHandle(hIn);
	CloseHandle(Hout);

效果如图。

上面加了一个死循环处理消息。和普通的Windows消息循环不一样,这里用ReadConsoleInput读取消息,在窗口焦点事件FOCUS_EVENT中重绘。

这下解决了上面的问题。注意字符光标在闪烁,表明程序在运行中,并没有结束。

第十步,添加死循环退出机制。

上面的死循环无法退出。虽然Ctrl+C可以退出程序,但这样会影响资源的释放。

添加代码。

        case KEY_EVENT:
 			if(inp.Event.KeyEvent.uChar.AsciiChar=='c' || inp.Event.KeyEvent.uChar.AsciiChar=='C')loop = false;
 			break;

这下按c键就可以退出了。注意和Ctrl+C的区别,c键只是退出循环,控制台窗口还在,Ctrl+C是连控制台窗口也关闭了。

第十一步,添加鼠标处理消息。

在死循环前加两个用于消息处理的变量。

    POINT point;
 	UINT message;

添加消息处理代码。

        case MOUSE_EVENT:
			if(inp.Event.MouseEvent.dwEventFlags == 0)//单击(包括按下或释放)
			{
				if(inp.Event.MouseEvent.dwButtonState == FROM_LEFT_1ST_BUTTON_PRESSED)//左键按下
				{
					message = WM_LBUTTONDOWN;
				}
				else if(inp.Event.MouseEvent.dwButtonState == RIGHTMOST_BUTTON_PRESSED)//右键按下
				{
					message = WM_CONTEXTMENU;
				}
				else if(inp.Event.MouseEvent.dwButtonState == FROM_LEFT_2ND_BUTTON_PRESSED)//滚轮
				{
					message = WM_MOUSEWHEEL;
				}
				//释放的时候:
				else if(inp.Event.MouseEvent.dwButtonState == 0)
				{
					message = WM_LBUTTONUP;
				}
			}
			else if(inp.Event.MouseEvent.dwEventFlags == DOUBLE_CLICK)//双击
			{
				message = WM_LBUTTONDBLCLK;
			}
			else if(inp.Event.MouseEvent.dwEventFlags == MOUSE_MOVED)//移动
			{
				message = WM_MOUSEMOVE;
			}
			else
			{
				message = WM_MOUSEMOVE;
			}
			GetCursorPos(&point);
			ScreenToClient(hWnd, &point);
			if(chart.OnEvent(hWnd, message, inp.Event.MouseEvent.dwControlKeyState, point.x + (point.y<<16))) MyDraw();
			break;

可以看到,这里先把保存在INPUT_RECORD变量中的鼠标事件信息转换为Windows的常规鼠标消息,然后调用CChart的消息处理函数OnEvent来处理消息。另外,INPUT_RECORD变量中并没有保存鼠标的像素位置信息,只有字符输入的行列信息,这里直接读取鼠标的屏幕坐标并转换为控制台窗口的客户区坐标,而鼠标消息的LPARAM变量低16位为客户区X坐标,高十六位为客户区Y坐标,采用移位的方式point.x + (point.y<<16)把point转换为LPARAM。

运行效果如图。

鼠标的各种消息,除了右键菜单外,都可以正常处理。控制台的右键菜单暂时还不知道怎么调出来。

各种对话框窗口也没有问题。

 今天的旧石器时代之旅就告一段落了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值