| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 3777 人关注过本帖, 6 人收藏
标题:Windows 控制台下的绘图方法
取消只看楼主 加入收藏
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
结帖率:100%
收藏(6)
已结贴  问题点数:20 回复次数:11 
Windows 控制台下的绘图方法
程序代码:
#include <Windows.h>
#include <ctime>
#include <cmath>
#include <conio.h>

// 全局常数及变量
const double PI(2 * asin(1.0));                     // 圆周率
const WCHAR K_ENTER(0x000D);                        // Enter键码
const RECT Border = { 10, 10, 620, 350 };           // 图框位置及尺寸
const POINT Origin = { 20, 170 };                   // 坐标原点位置
const double Scale(80.0);                           // 缩放比例
const double Increase(0.0001);                      // 自变量微元增量dx

HDC hDC(GetDC(GetConsoleWindow()));                 // 绘图设备

// 函数原型
void DrawBorder(void);                              // 绘制图框
void DrawAxes(void);                                // 绘制坐标轴
void DrawGraph(void);                               // 绘制图像
void Wait(void);                                    // 等待按键

// 程序入口
void wmain(void)
{
    DrawBorder();
    DrawAxes();
    DrawGraph();
    Wait();
    ReleaseDC(NULL, hDC);
}

void DrawBorder(void)
{
    HPEN hPen(CreatePen(PS_SOLID, 1, RGB(0,255,0)));
    HBRUSH hBrush(CreateSolidBrush(RGB(0,0,0)));
    SelectObject(hDC, hPen);
    SelectObject(hDC, hBrush);
    Rectangle(hDC, Border.left, Border.top, Border.right, Border.bottom);
    DeleteObject(hPen);
    DeleteObject(hBrush);
}

void DrawAxes(void)
{
    HPEN hPen(CreatePen(PS_SOLID, 1, RGB(0, 255, 255)));
    SelectObject(hDC, hPen);
    MoveToEx(hDC, Origin.x, Origin.y, NULL);
    LineTo(hDC, Border.right - 10, Origin.y);
    DeleteObject(hPen);
}

void DrawGraph(void)
{
    HPEN hPen(CreatePen(PS_SOLID, 1, RGB(255, 255, 0)));
    SelectObject(hDC, hPen);
    MoveToEx(hDC, Origin.x, Origin.y, NULL);
    for (double x = 0.0; x <= 2 * PI; x += Increase)
    {
        LineTo(hDC, Origin.x + static_cast<int>(x * Scale), Origin.y - static_cast<int>(sin(x) * Scale));
    }
    DeleteObject(hPen);
}

void Wait(void)
{
    const WCHAR msg[] = L"按<Enter>键结束程序";
    RECT rect = { Border.left, Border.bottom + 20, Border.right, Border.bottom + 50 };
    SetTextColor(hDC, RGB(0,0,255));
    SetBkColor(hDC, TRANSPARENT);
    DrawTextW(hDC, msg, _countof(msg), &rect, DT_CENTER);

    HANDLE hConsole(GetStdHandle(STD_OUTPUT_HANDLE));   // 控制台句柄
    CONSOLE_CURSOR_INFO CursorInfo;
    GetConsoleCursorInfo(hConsole, &CursorInfo);
    CursorInfo.bVisible = false;
    SetConsoleCursorInfo(hConsole, &CursorInfo);
    while (_getwch() != K_ENTER)
    {
        ;
    }
    CursorInfo.bVisible = true;
    SetConsoleCursorInfo(hConsole, &CursorInfo);
}


图片附件: 游客没有浏览图片的权限,请 登录注册


[ 本帖最后由 TonyDeng 于 2012-3-27 15:19 编辑 ]
搜索更多相关主题的帖子: Windows 
2012-03-27 02:14
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
收藏
得分:0 
下面是加上顺带求定积分的代码:

程序代码:
#include <Windows.h>
#include <cstdio>
#include <ctime>
#include <cmath>
#include <conio.h>

// 全局常数及变量
const double PI(2 * asin(1.0));                     // 圆周率
const WCHAR K_ENTER(0x000D);                        // Enter键码
const RECT Border = { 10, 10, 620, 350 };           // 图框位置及尺寸
const POINT Origin = { 20, 170 };                   // 坐标原点位置
const double Scale(80.0);                           // 缩放比例
const double Increase(0.0001);                      // 自变量微元增量dx

HDC hDC(GetDC(GetConsoleWindow()));                 // 绘图设备

// 函数原型
void DrawBorder(void);                              // 绘制图框
void DrawAxes(void);                                // 绘制坐标轴
void DrawGraph(void);                               // 绘制图像
void Wait(void);                                    // 等待按键

// 程序入口
void wmain(void)
{
    DrawBorder();
    DrawAxes();
    DrawGraph();
    Wait();
    ReleaseDC(NULL, hDC);
}

void DrawBorder(void)
{
    HPEN hPen(CreatePen(PS_SOLID, 1, RGB(0,255,0)));
    HBRUSH hBrush(CreateSolidBrush(RGB(0,0,0)));
    SelectObject(hDC, hPen);
    SelectObject(hDC, hBrush);
    Rectangle(hDC, Border.left, Border.top, Border.right, Border.bottom);
    DeleteObject(hPen);
    DeleteObject(hBrush);
}

void DrawAxes(void)
{
    HPEN hPen(CreatePen(PS_SOLID, 1, RGB(0, 255, 255)));
    SelectObject(hDC, hPen);
    MoveToEx(hDC, Origin.x, Origin.y, NULL);
    LineTo(hDC, Border.right - 10, Origin.y);
    DeleteObject(hPen);
}

void DrawGraph(void)
{
    HPEN hPen(CreatePen(PS_SOLID, 1, RGB(255, 255, 0)));
    SelectObject(hDC, hPen);
    MoveToEx(hDC, Origin.x, Origin.y, NULL);
    double integral(0.0);
    for (double x = 0.0; x <= 2 * PI; x += Increase)
    {
        double fun_value(sin(x));
        LineTo(hDC, Origin.x + static_cast<int>(x * Scale), Origin.y - static_cast<int>(fun_value * Scale));
        if (x <= PI)
        {
            integral += fun_value * Increase;      // 求定积分
        }
    }
    DeleteObject(hPen);

    SetTextColor(hDC, RGB(255,0,255));
    SetBkColor(hDC, TRANSPARENT);
    WCHAR buffer[1024];
    swprintf_s(buffer, 20, L"半周期的面积=%.4f", integral);
    TextOutW(hDC, Border.left + 10, Border.bottom - 30, buffer, wcslen(buffer));
}

void Wait(void)
{
    const WCHAR msg[] = L"按<Enter>键结束程序";
    RECT rect = { Border.left, Border.bottom + 20, Border.right, Border.bottom + 50 };
    SetTextColor(hDC, RGB(255,255,255));
    SetBkColor(hDC, TRANSPARENT);
    DrawTextW(hDC, msg, _countof(msg), &rect, DT_CENTER);

    HANDLE hConsole(GetStdHandle(STD_OUTPUT_HANDLE));   // 控制台句柄
    CONSOLE_CURSOR_INFO CursorInfo;
    GetConsoleCursorInfo(hConsole, &CursorInfo);
    CursorInfo.bVisible = false;
    SetConsoleCursorInfo(hConsole, &CursorInfo);
    while (_getwch() != K_ENTER)
    {
        ;
    }
    CursorInfo.bVisible = true;
    SetConsoleCursorInfo(hConsole, &CursorInfo);
}


图片附件: 游客没有浏览图片的权限,请 登录注册


[ 本帖最后由 TonyDeng 于 2012-3-28 02:05 编辑 ]

授人以渔,不授人以鱼。
2012-03-27 15:58
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
收藏
得分:0 
回复 5楼 小鱼儿c
你们平时学的就是控制台编程呀

授人以渔,不授人以鱼。
2012-03-27 17:02
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
收藏
得分:0 
以下是引用C_596322153在2012-3-27 21:06:48的发言:

版主 句柄有啥用啊 前几天看api一点不懂  关于windows下图像编程   什么 句柄 消息 一类的 有什么好书推荐啊

句柄简单来说就是一个ID数字,用来识别不同“对象”的。面向对象的编程,定义了很多“类”,类相当于蓝图,而根据类制造出来的实体就叫“对象”,每份蓝图出不同的实体对象,同一个类可以制造多个对象,为了识别同一个类下对应的不同对象、以及不同类不同的对象,就需要给每个对象都分配一个ID,作为这个实体的唯一标识。好比我们的身份证号码,同一行政区划的号码都会反映在身份证号码中,但身份证号码有附加有各种各样的唯一识别码,用以区分。句柄现在定为整数,但编程时最好不要假想它是整数,就如不要把指针值当作整数一样,因为在概念上它们毕竟不是整数,而是独特的逻辑概念(正如不应该把逻辑真假视为整数0/1一样)。

在Windows的架构中,每种类都是一个实现库中有基本运行模版的,所有基于同一个类生成的对象,其行为代码全部从这个实现库中抽取,因此,尽管我们对同一个类定义了很多不同的对象实体(比如开了若干个窗口),但这些对象的基本行为其实是用同一份代码实现的,不同的动作,其实是我们设定了不同的参数,用参数来驱动代码,不同的参数表现出不同的行为,但究其实,真实的执行代码就只有一份!因此,你开不同的窗体,传入每个窗体的Title字符串,类代码就会把这些具体的字符串呈现在窗体的标题栏,看起来就是不同的对象在工作,但其实只有一份代码在工作,这份代码要应付你开设的所有的窗体的动作。这就是面向对象的编程,你写的代码,要能适应不同的动作需求,但代码就只有一份,被数据驱动。Windows使用句柄,是要把每个对象实体的数据用查表法归属到代码中,相同句柄下的数据,执行起来就是对同一个对象操作的。

“消息”,就是那些用来驱动执行代码的参数。Windows系统下应用程序的执行,其实是Windows根据消息调用程序代码,是Windows在执行程序,不是程序自己在执行,程序能做的,是发出消息通知Windows想做什么,然后Windows在同意的情况视具体情形在适当时候满足你的需求。消息的基本原理,其实就是在一个死循环中不断查询是否有某种约定的消息数据出现了,如果有,就执行对应的功能,如果没有,就查询下一个,就这样不断死循环,把所有消息都处理完。比如在我这里的程序中,其实那个main()主程序就有一个叫Wait()的死循环,它在开始执行完“初始化”把图像呈现出来后,就处于等待状态,除非接收到消息(按下Enter键),否则它什么也不做(如果这里只有一个消息,还不能让你明白,那么看看这里https://bbs.bccn.net/thread-364017-1-1.html 6楼的main()函数代码,就由四个消息驱动,计时、发出变换图像的信号、是否有按键及按了什么键)。Windows程序的主程序,其实就是这么一个死循环,在循环内部不断地搜寻有什么消息出现,你可以在这个程序中添加各种各样不同的消息(不要死守着Windows的消息,Windows是根据这样的理念制造的程序产品,不是它的专利,消息是程序员自创的,不是只有Windows定义的那些东西才叫消息)。

事实上,“消息”的机制并不新鲜,也不是从C++才开始有的,早在C时代就有了。printf()和scanf()函数的编制,其实就是典型的消息驱动机制,通过约定的格式字符串,指示函数内部处理数据的动作,这就是这两个函数之所以必须要附带格式字符串而且要严格的原因,不过这两个函数约定的消息是死的,它能处理内置数据类型,但无法用同样的机制处理自定义数据类型,所以后来才用C++的多态性来实现这种需求,从而废弃了这两个函数。不过,思想真的并不新鲜,C++是在C基础上发展起来的,当然有很多共同基础,很多人没意识到这一点,才总说C和C++如何这般的不同,以至于走到极端要纯粹的C或C++,那是偏狭了。

现在,你可以看看我这个程序的写法。我把各种可能变动的参数都集中写在全局变量那里,如果你需要修改,就在那里集中修改,而基本上不用动后面的代码,这样,你写程序时,集中精力写好一份代码就可以了,以后的变动,交给数据去做,只要你代码写好了,就不用反复地写类似的代码。编程所要学的,其实就是养成这么一种理念,不要总喜欢做同类的事情,写链表,就一辈子在弄各种各样的链表,学会写链表算法,不表示懂编程了。

算法再精妙,终究也只需写一次,以后不会再用到了(所以我们应该把精力放在那些将来会经常遇到的算法上,比如基本的排序、筛选、查寻、比较、读写文件等等,而不要总弄那些没多少机会遇到的算法,常用的算法,算法书上都有伪代码,用什么语言都能实现,需要时花个几天时间把伪代码实现了就可以,不用在大学花数年时间来练指法,那些是将来可以现学的,但思想和思维是必须花大力气巩固培养的),这就是C++专家建议我们采用STL标准库的目的,他们集全世界的精英力量构建好了那些算法库和数据结构,你自己写的代码无论如何都不及他们的优秀,何必花那些力气去弄精通它们,了解基本精神和大致的原理即可,无需自己去实现那些代码,除非你要向那些精英挑战、兴趣所在,那当然是你的自由和权力了。

[ 本帖最后由 TonyDeng 于 2012-3-27 22:40 编辑 ]

授人以渔,不授人以鱼。
2012-03-27 21:34
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
收藏
得分:0 
你指的是MFC中动态生成控件的那些东西?

授人以渔,不授人以鱼。
2012-03-28 01:50
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
收藏
得分:0 
以下是引用moonnight在2012-3-27 18:52:33的发言:

发现现在还看不懂。。。。

都是很基本的C代码呀,除了使用了C++的初始化和变量声明语法之外,没有用C++的其余特性。这个程序是完全公开代码的,没有我自编的函数或库,在普通的Windows编译器下编译,应能通过。如果是那些函数你没见过,那就是Windows的API函数,结合运行结果猜就是了。而且最好的办法,是可以动手修改代码,验证自己的猜测是否正确——事实上我也是这样猜那些API函数的确切用法的。

对一门语言的若干细节有不了解的地方,这很正常,只要肯动手做实验(计算机编程最好的地方是能够让你无限制地实验,其他行业可不能这样),总能摸得到底细。C语言(以及将来的C++类库)有那么多函数、类和数据结构,其总量比Windows API多得多,谁也不可能完全熟悉和掌握,只能是自己用到什么就学什么,能用即可,要全部精通是不可能的,也没必要。毕竟人是活的,总不可能被语言憋死,不懂成语、典故,也不会影响你用大白话表达意思,最多没什么文采罢了,但总能表达意思,完成任务。比如,我就总查不到C标准库中圆周率的宏定义,又不想自己写那个不太精确适用的3.1415926(在CLR类库倒真有这个宏,叫M_PI,但我现在并不用CLR版本写这个程序,故没用),就只能取巧用反正弦函数求出圆周率代入运算了,这样做,总不值得被很精通语言细节的人耻笑,也没什么可笑的,将来某日知道了,把它改回来,也就可以了。程序不可能十全十美的,总要修改、维护,把它当自己的孩子,生出来就要不断培育它成长,直至小程序成为一个具备一定功能的产品——以前求伯君他们,也是从一些很小的程序开始写出其成名作的,求伯君就是从一个打印驱动模块开始写出其中文处理系统和文字编辑器,都是因为对自己的作品倾注心血而来。总之,不要因为老师打了个勾,就完事了,那样学习,是不行的,又不是幼儿园,比赛谁得的红花多。

[ 本帖最后由 TonyDeng 于 2012-3-28 02:43 编辑 ]

授人以渔,不授人以鱼。
2012-03-28 02:20
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
收藏
得分:0 
MFC我没用过,但一理通,百理明,从其他语言的用法,也可以知道基本的原理,根据原理,去追索资料,总能找到。

所谓“动态生成控件”,无非是在程序运行时向某个“容器”(Container类型的控件)添加控件,窗体(Form)就是一个最基本的容器,表格(Grid)也是,这是一类可以向其中添加物件的容器。凡是这类容器,必然有一个追加对象(AddObject)的方法,比如某个Form的实体ID叫Form1,那么通常调用Form1.AddObject()方法,就是向这个容器添加参数中指定的控件实体,因此关键是在哪个容器中发出AddObject()指令,你想向哪个容器加控件,就把这个容器的动态添加控件的方法找出来(一定有的,因为这是该控件设计的必需品)。在Visual FoxPro中这是AddObject方法,在 C#中这是.AddControl()方法。通过Add操作,所谓动态生成的控件ID,就是把从祖先到父亲及自己的ID串连起来,标识出有继承关系的ID,构成一条控件链,供操作系统查寻使用,Windows自己维护这种对象数据库。

以上是运行时的动态生成原理,在可视化的设计模式IDE中,是IDE执行这种Add操作,把控件动态添加到对应的地方,这时,它向你隐瞒了真实的代码步骤,因此,很多依靠可视化(Visual)编写程序的人,就很难了解在没有IDE环境下如何仅靠代码动态添加控件,从而产生“面向对象编程就是用鼠标拖拖拉拉”的错觉。事实上,我用Visual FoxPro编程,就从来不使用这种可视化编程模式,所有控件都是在代码中生成的,因此很清楚这类东西的运作机制。用这样的经验,在上也看到同样的做法,故此基本上可以确定MFC也必定是这样。事实上,无论Visual FoxPro,还是和C#、MFC,在根本底层上,都是微软的那一套运行时库,它们编程出来后所执行的效果,全部是同一个控件类产生的(比如Form控件,Visual FoxPro是很古老的产品,但现在用它的Form控件生成的窗体,却呈现出Win7窗体的半透明标题栏和边框模式,可见实际执行的就是Windows当前版本的控件)。所以,在底层原理上,必定是相通的。

你按照这样的思路,在MFC文档中查找看看,必定有相关资料。关键之处无非是两点:首先要确定你只能向容器性质的控件中动态添加控件,你可以把TextBox添加到Grid中,但无法在TextBox中再添加一个TextBox,把控件的性质搞清楚;其次是找到容器类添加子类的方法(Grid控件中必定有,而TextBox控件中肯定没有),看文档。动态生成的控件,一定是用new方法在堆上申请的一个对象,然后把这个对象的句柄返回给你,添加在它的父类的孩子清单上。

其实,在传统C++中写面向对象的控制台程序,也是用这种思路识别对象树,就如链表的原理一样,总有一个指向父项的标识和指向后裔的标识,不管那是不是可视化的对象,只要有包容关系,就一定要设计这样的字段,因为这是逻辑的要求,闭着眼睛都把它先写下来,至于用不用得到、如何用,以后再说,缺了必需品,以后伤筋动骨的可能性就越大,只要你挂着这么一个“未使用”的东西,随时都会提醒你有什么未做完,自然会想办法把它做了。因此设计初期就要审慎,对常用的类,要通过大量的小程序应用来测试,稳定下来之后才能应用于大系统。花两三年的时间,积累这么一套自己的工具库,你以后编程就轻松多了。把精力和时间花在这方面,好过就题做题。不想把组件代码的功能扩展到最大适用范围的,那是没打算以后从事这行当的,没意识就是没资质、不适合,以后做重复代码的疲劳,很快就会把你的兴趣消磨净尽——不要把兴趣当职业,这是社会上很流行的忠告了。

【题外话,以我对Visual FoxPro的理解,在本机程序范畴,它强大到足以应付一切可能的需要,除了不能在Web浏览器上运行自己的代码,它可以把IE的内核作为控件嵌在自己的界面上故可以浏览网页,它有API接口可以做到与系统最底层密切合作,而使用却比VC轻松得多,故此我说不需要用C/C++编写Windows程序,不必要花那些精力。在内部机制上,我现在已经看出C#有VFP的影子,除了提供更多更新的功能,在基本原理上竟然是那么相似。好像从什么地方看到,似乎微软把VFP开发团队的核心人员调到C#开发部了。以下是我用VFP提取的点阵字模的图案,如果用C实现这样的效果,耗费的精力要多很多(连空行算也只有130行),因为以前就是要从现成的点阵字库中切割偏旁部首组合GB2312编码中没有的汉字传输到LED显示屏中显示,要看到字模的数据影像才好做功课,曾经写过那样的东西。为了节省内存,在英文DOS下,就是用这样的东西显示中文的,这就是CCDOS的原理。在现在,如果要在控制台下显示特殊的字体,而系统又没有对应的等宽字体库,就可以用同样的办法做,不必总是单调的宋体字。】

图片附件: 游客没有浏览图片的权限,请 登录注册


[ 本帖最后由 TonyDeng 于 2012-3-28 04:27 编辑 ]

授人以渔,不授人以鱼。
2012-03-28 03:06
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
收藏
得分:0 
动态生成,就是如链表那样动态分配数据点的,因为事先不可能知道实际需要多少个数据,而只能动态变化。动态控件是从类蓝图中生成的,与我们平时用malloc(5*sizeof(int))生成5个int数据一样,支持面向对象的语言也能在运行时通过new动态生成一些数据实体。当你写一个Grid控件,里面的栏(Column)数目要根据运行情况动态变化时,比如此一时刻需要10栏,彼一时刻要从中插入1栏、或删掉其中1栏,就要能够动态生成新的Column控件来呈现数据,这是无法在设计时预先分配好的。有些时候,要设计大量的同类控件(比如15楼那个图的256个小方格),是不可能在IDE中手动添加的,也要用动态生成来构造。

授人以渔,不授人以鱼。
2012-03-28 13:22
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
收藏
得分:0 
你用的什么编译器?这是Windows下使用Win API的程序,不要在别的环境中编译。

授人以渔,不授人以鱼。
2012-04-05 18:30
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
收藏
得分:0 
回复 22楼 michaelc
这是C++程序,文件扩展名为.cpp,不是.c。

授人以渔,不授人以鱼。
2012-04-05 22:10
快速回复:Windows 控制台下的绘图方法
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.027597 second(s), 11 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved