C语言图形化界面——含图形、按钮、鼠标、进度条等部件制作(带详细代码、讲解及注释)

目录

  • 0.引言
  • 1.素材准备
  • 2.编程
    • 2.1.创建你的界面
    • 2.2.创建按钮
    • 2.3.鼠标操作
      • 2.3.1.单击特效
      • 2.3.2.光标感应
      • 2.3.3.进度条
  • 3.完整代码及效果

0.引言

\qquad看了CSDN上很多关于C程序图形化界面的介绍,有的代码繁琐难解,不方便调试修改;有的不够详细。本文提供的代码简单、易于移植、容易理解,望急需使用C语言制作图形化界面的朋友采纳。
\qquad对easyx尚不熟悉的朋友不需要担心,我敢打包票它只需10分钟就可以上手,而它为你节省的时间可能是3个小时甚至更多。关于easyx的简单应用请参考一篇我以前写的关于C程序可视化的博文。
→【C语言实现动画控制】←
\qquad本文的讲解是循序渐进的,读者应该重点关注每个步骤的理解,两步之间代码的变化,即可完全理解本文。

1.素材准备

  1. easyx的下载链接如下:(本文使用的版本是2014冬至版)
    https://www.easyx.cn/downloads/
    注:使用easyx需要注意它兼容的编译器(下载的帮助文件会写),不同的easyx兼容的编译器不同,但总是和visual C++6兼容(和字符编码有关),本文以visual C++6编译器为例书写代码。
  2. easyx的最新英文帮助文档链接(下载2014冬至版会自带中文帮助文档):
    https://docs.easyx.cn/en-us/intro
  3. 如果你成功下载了easyx2014冬至版,那么解压后把头文件(easyx.h和graphic.h)和lib文件(amd64)分别放在VC文件夹默认的include文件夹和lib文件夹中。右键你的VC程序,选择打开文件所在位置,然后找到MFC文件夹,友情提供两个文件夹的位置截图。
    include
    lib
  4. 建议编译的C文件以cpp后缀保存。

2.编程

2.1.创建你的界面

\qquad创建一个480×360的窗口,我们需要使用initgraph()函数,闲言少叙,让我们直接看一段代码:

#include <graphics.h>              // 引用图形库头文件
#include <conio.h>
#include <stdio.h>
#include <windows.h>				//用到了定时函数sleep()
#include <math.h>int main()
{int i;short win_width,win_height;//定义窗口的宽度和高度win_width = 480;win_height = 360;initgraph(win_width,win_height);//初始化窗口(黑屏)for(i=0;i<256;i+=5){setbkcolor(RGB(i,i,i));//设置背景色,原来默认黑色cleardevice();//清屏(取决于背景色)Sleep(15);//延时15ms}closegraph();//关闭绘图界面
}

\qquad这段代码很容易理解,运行这段程序,就会出现逐渐明亮的屏幕。因为不断刷新背景色为RGB(i,i,i)RGB(i,i,i)RGB(i,i,i)。C语言中的颜色使用十六进制表示的,RGB函数可以将0~255范围内的三个整数三原色转换成这个十六进制。
\qquadcleardevice()函数用于清屏,是界面内所有元素都被清空,一般只会在初始化出现。
\qquadSleep()是毫秒级延迟,当然界面变亮时间不一定是准确的15ms×255/5=0.765s,因为其他语句还需要执行时间。
\qquadclosegraph():关闭绘图界面。注意,如果初始化了绘图界面但没有在主函数结束前关闭它,可能会引发一些莫名其妙的错误!所以这个函数一定要有!

2.2.创建按钮

\qquad我们尝试在界面创建几个按钮,按钮需要的操作是绘制矩形和打印文字。虽然看着简单,但是里面还是有点学问,为了方便大家理解,还是先放上代码和注释。

#include <graphics.h>              // 引用图形库头文件
#include <conio.h>
#include <stdio.h>
#include <windows.h>				//用到了定时函数sleep()
#include <math.h>
int r1[]={30,20,130,60};//输入按钮的矩形参数
int r2[]={170,20,220,60};//运行按钮的矩形参数
int r3[]={260,20,310,60};//退出按钮的矩形参数
int main()
{int i;short win_width,win_height;//定义窗口的宽度和高度win_width = 480;win_height = 360;initgraph(win_width,win_height);//初始化窗口(黑屏)for(i=0;i<256;i+=5){setbkcolor(RGB(i,i,i));//设置背景色,原来默认黑色cleardevice();//清屏(取决于背景色)Sleep(15);//延时15ms}RECT R1={r1[0],r1[1],r1[2],r1[3]};//矩形指针R1RECT R2={r2[0],r2[1],r2[2],r2[3]};//矩形指针R2RECT R3={r3[0],r3[1],r3[2],r3[3]};//矩形指针R3LOGFONT f;//字体样式指针gettextstyle(&f);					//获取字体样式_tcscpy(f.lfFaceName,_T("宋体"));	//设置字体为宋体f.lfQuality = ANTIALIASED_QUALITY;    // 设置输出效果为抗锯齿  settextstyle(&f);                     // 设置字体样式settextcolor(BLACK);				//BLACK在graphic.h头文件里面被定义为黑色的颜色常量drawtext("输入参数",&R1,DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R1内输入文字,水平居中,垂直居中,单行显示drawtext("运行",&R2,DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R2内输入文字,水平居中,垂直居中,单行显示drawtext("退出",&R3,DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R3内输入文字,水平居中,垂直居中,单行显示setlinecolor(BLACK);rectangle(r1[0],r1[1],r1[2],r1[3]);rectangle(r2[0],r2[1],r2[2],r2[3]);rectangle(r3[0],r3[1],r3[2],r3[3]);system("pause");//暂停,为了显示closegraph();return 0;
}

在这里插入图片描述
\qquad这里需要特别介绍的是矩形指针pRectpRectpRect,它使用句柄RECT定义,并且不可以中途再次赋值。之所以要设置矩形指针了为了打印字体的时候以矩形为边界自动填充。它的格式是RECT r={X1,Y1,X2,Y2},X1和X2是矩形的左边和右边的横坐标,Y1和Y2是矩形的上边和下边的纵坐标,这一点和rectangle()绘制空心矩形函数参数排列一致。后面的DT_CENTER | DT_VCENTER | DT_SINGLELINE就是描述填充格式的常量。使用drawtext书写文字不需要再计算文字的坐标和设置大小,会方便很多。
\qquadLOGFONT是字体样式指针,通过gettextstyle()函数来获取当前的字体类型,再通过settextstyle()函数加以设置。这里只修改了字体的名称和显示质量,还可以修改斜体、下划线等属性,更详细的部分请参考帮助文档。

2.3.鼠标操作

2.3.1.单击特效

\qquad作为一个图形化界面的C程序,鼠标操作总不能少吧。在讲解程序前先别着急,简单为大家科普一下鼠标事件:
\qquad鼠标是输入设备,只要发生以下的事件,就会暂存在鼠标消息列表中,我们的操作系统就会依次响应列表中的鼠标消息事件,常用的鼠标事件如下:

  • WM_MOUSEMOVE——鼠标移动
  • WM_MOUSEWHEEL——鼠标滚轮滚动
  • WM_LBUTTONDOWN——鼠标左键按下
  • WM_LBUTTONUP——鼠标左键弹起
  • WM_LBUTTONDBLCLK——鼠标左键双击
  • WM_RBUTTONDOWN——鼠标右键按下
  • WM_RBUTTONUP——鼠标右键弹起
  • WM_RBUTTONDBLCLK——鼠标左键双击
  • WM_MBUTTONDOWN——鼠标中键按下
  • WM_MBUTTONUP——鼠标中键弹起
  • WM_MBUTTONDBLCLK——鼠标中键双击
    \qquad我们只需要根据不断获取鼠标消息队列的消息并根据消息依次进行响应即可。

\qquad相信大家已经迫不及待了,那么请看下面一个简单的程序。

#include <graphics.h>              // 引用图形库头文件
#include <conio.h>
#include <stdio.h>
#include <windows.h>				//用到了定时函数sleep()
#include <math.h>
int r1[]={30,20,130,60};//输入按钮的矩形参数
int r2[]={170,20,220,60};//运行按钮的矩形参数
int r3[]={260,20,310,60};//退出按钮的矩形参数
int main()
{int i;short win_width,win_height;//定义窗口的宽度和高度win_width = 480;win_height = 360;initgraph(win_width,win_height);//初始化窗口(黑屏)for(i=0;i<256;i+=5){setbkcolor(RGB(i,i,i));//设置背景色,原来默认黑色cleardevice();//清屏(取决于背景色)Sleep(15);//延时15ms}RECT R1={r1[0],r1[1],r1[2],r1[3]};//按钮1的矩形区域RECT R2={r2[0],r2[1],r2[2],r2[3]};//按钮2的矩形区域RECT R3={r3[0],r3[1],r3[2],r3[3]};//按钮2的矩形区域LOGFONT f;gettextstyle(&f);					//获取字体样式_tcscpy(f.lfFaceName,_T("宋体"));	//设置字体为宋体f.lfQuality = ANTIALIASED_QUALITY;    // 设置输出效果为抗锯齿  settextstyle(&f);                     // 设置字体样式settextcolor(BLACK);				//BLACK在graphic.h头文件里面被定义为黑色的颜色常量drawtext("输入参数",&R1,DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R1内输入文字,水平居中,垂直居中,单行显示drawtext("运行",&R2,DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R2内输入文字,水平居中,垂直居中,单行显示drawtext("退出",&R3,DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R3内输入文字,水平居中,垂直居中,单行显示setlinecolor(BLACK);rectangle(r1[0],r1[1],r1[2],r1[3]);rectangle(r2[0],r2[1],r2[2],r2[3]);rectangle(r3[0],r3[1],r3[2],r3[3]);MOUSEMSG m;//鼠标指针setrop2(R2_NOTXORPEN);//二元光栅——NOT(屏幕颜色 XOR 当前颜色)while(true){m = GetMouseMsg();//获取一条鼠标消息if(m.uMsg==WM_LBUTTONDOWN){for(i=0;i<=10;i++){setlinecolor(RGB(25*i,25*i,25*i));//设置圆颜色circle(m.x,m.y,2*i);Sleep(25);//停顿2mscircle(m.x,m.y,2*i);//抹去刚刚画的圆}FlushMouseMsgBuff();//清空鼠标消息缓存区}}system("pause");//暂停,为了显示closegraph();return 0;
}

在这里插入图片描述
\qquad每点击鼠标以下,应该可以看到鼠标点击处有一个逐渐扩大并淡出的圆(截图无法清晰,在画面的中右侧),当循环体内Sleep的视觉大于20ms后视觉效果很强。
\qquad每响应一次鼠标左键单击事件,都会调用一次清空鼠标消息缓存区的函数FlushMouseMsgBuff(),如果没有这个函数会怎么样呢?如果我们快速连续地单击鼠标左键N次,那么特效就会播放N次,如果特效播放速度比单击的速度慢,那么即使你停下来了,程序仍然会接着播放单击特效,因为你的左键单击仍然在鼠标的消息队列m.uMsg中的鼠标消息没有响应完。
\qquad这里需要解释的是一个二元光栅设置函数setrop2(),二元光栅是混合背景色和当前颜色的模式。我们这里采用的方式是同或(NOT XOR)的方式,若底色为白色(1),则当前颜色不变;若底色是黑色(0),则当前颜色反色。为什么需要采用这种方式呢?因为我们在第二次抹去原来的圆的时候不能采用白色,否则如果背景色原来就为黑(比如按钮和文字),就也会被抹成白色。而背景色与任意一个颜色同或两次都为其本身,即可起到还原背景色的效果。这里的背景色与cleardevice()前面那个背景色不同,这里的是指执行这一条绘画指令之前屏幕上的颜色。

2.3.2.光标感应

\qquad我们希望鼠标移到按钮上时按钮会有所变化,移开按钮时又会回到原样。这里我们采用一种简单的填充颜色的方法,就是按钮变色。我们需要解决一个问题就是按钮变色了但是按钮的文字不能被覆盖,那么我们还是需要使用到二元光栅。只是我们这次的模式改成了同或。
\qquad为了方便起见,存放三个按钮的数组我们合并为了一个二维数组,在鼠标事件中更容易使用和分配任务。

#include <graphics.h>              // 引用图形库头文件
#include <conio.h>
#include <stdio.h>
#include <windows.h>				//用到了定时函数sleep()
#include <math.h>
int r[3][4]={{30,20,130,60},{170,20,220,60},{260,20,310,60}};//三个按钮的二维数组int button_judge(int x,int y)
{if(x>r[0][0] && x<r[0][2] && y>r[0][1] && y<r[0][3])return 1;if(x>r[1][0] && x<r[1][2] && y>r[1][1] && y<r[1][3])return 2;if(x>r[2][0] && x<r[2][2] && y>r[2][1] && y<r[2][3])return 3;return 0;
}
int main()
{int i,event=0;short win_width,win_height;//定义窗口的宽度和高度win_width = 480;win_height = 360;initgraph(win_width,win_height);//初始化窗口(黑屏)for(i=0;i<256;i+=5){setbkcolor(RGB(i,i,i));//设置背景色,原来默认黑色cleardevice();//清屏(取决于背景色)Sleep(15);//延时15ms}RECT R1={r[0][0],r[0][1],r[0][2],r[0][3]};RECT R2={r[1][0],r[1][1],r[1][2],r[1][3]};RECT R3={r[2][0],r[2][1],r[2][2],r[2][3]};LOGFONT f;gettextstyle(&f);					//获取字体样式_tcscpy(f.lfFaceName,_T("宋体"));	//设置字体为宋体f.lfQuality = ANTIALIASED_QUALITY;    // 设置输出效果为抗锯齿  settextstyle(&f);                     // 设置字体样式settextcolor(BLACK);				//BLACK在graphic.h头文件里面被定义为黑色的颜色常量drawtext("输入参数",&R1,DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R1内输入文字,水平居中,垂直居中,单行显示drawtext("运行",&R2,DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R2内输入文字,水平居中,垂直居中,单行显示drawtext("退出",&R3,DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R3内输入文字,水平居中,垂直居中,单行显示setlinecolor(BLACK);rectangle(r[0][0],r[0][1],r[0][2],r[0][3]);rectangle(r[1][0],r[1][1],r[1][2],r[1][3]);rectangle(r[2][0],r[2][1],r[2][2],r[2][3]);MOUSEMSG m;//鼠标指针while(true){m = GetMouseMsg();//获取一条鼠标消息switch(m.uMsg){case WM_MOUSEMOVE:setrop2(R2_XORPEN);setlinecolor(LIGHTCYAN);//线条颜色为亮青色setlinestyle(PS_SOLID, 3);//设置画线样式为实现,10磅setfillcolor(WHITE);//填充颜色为白色if(button_judge(m.x,m.y)!=0){if(event != button_judge(m.x,m.y)){event = button_judge(m.x,m.y);//记录这一次触发的按钮fillrectangle(r[event-1][0],r[event-1][1],r[event-1][2],r[event-1][3]);//有框填充矩形(X1,Y1,X2,Y2)}}else{if(event != 0)//上次触发的按钮未被修正为原来的颜色{fillrectangle(r[event-1][0],r[event-1][1],r[event-1][2],r[event-1][3]);//两次同或为原来颜色event = 0;}}break;case WM_LBUTTONDOWN:setrop2(R2_NOTXORPEN);//二元光栅——NOT(屏幕颜色 XOR 当前颜色)for(i=0;i<=10;i++){setlinecolor(RGB(25*i,25*i,25*i));//设置圆颜色circle(m.x,m.y,2*i);Sleep(30);//停顿30mscircle(m.x,m.y,2*i);//抹去刚刚画的圆}break;FlushMouseMsgBuff();//清空鼠标消息缓存区}}system("pause");//暂停,为了显示return 0;
}

在这里插入图片描述
\qquad这里我们运用了两次设置二元光栅的函数setrop2,在鼠标的移动条件内(case MOUSEMOVE),我们使用的是屏幕颜色和当前颜色异或,这是为什么呢?因为fillrectangle()函数是画一个有框填充矩形,我们让这个有框填充矩形和原按钮的一样大。由于线条的颜色为亮青色,填充颜色为白色(1),白色的填充颜色和屏幕颜色异或,取的是屏幕颜色的反色。按钮的边框是黑色(0),它与亮青色异或,则会保留原来的亮青色。
\qquad与同或一样,异或两次等于没有执行操作,所以可以还原到原屏幕画布的颜色。

2.3.3.进度条

\qquad既然涉及到进度条了,那么就应该涉及到正式程序了,这里我不想涉及太多的专业知识影响大家理解,但又不能让程序运行得太快以至于看不出进度条的变化,所以我设计了一个简单的弹性球轨迹作图程序。
\qquad假设球半径为R,初始高度为h0h_0h0,初速度为0(自由落体),非弹性碰撞时能量损失率为α\alphaα。计算部分子函数如下:

int simulation()
{float dt = 0.01;//仿真间隔10mslong int N = (long int)(sim_t/dt);//迭代次数float *h=(float*)calloc(N,sizeof(float));//高度float *v=(float*)calloc(N,sizeof(float));//速度(竖直方向)long int i;//迭代变量for(i=1;i<N;i++){if(h[i-1]>R)//未发生碰撞{v[i]=v[i-1]-9.8*dt;//速度计算}else//发生碰撞,动能损失alpha,速度损失alpha的开方{v[i]=-sqrt(alpha)*v[i-1];	}}free(h);free(v);//释放内存return 0;
}

\qquad当然,我们还需要绘图网格,定义绘图网格的函数如下:

void init_figure()
{int i;setrop2(R2_COPYPEN);//当前颜色setlinecolor(BLACK);setlinestyle(PS_SOLID);//实线rectangle(30,100,420,330);//外框线setlinestyle(PS_DOT);//点线for(i=30+39;i<420;i+=39){line(i,100,i,330);//竖直辅助线}for(i=100+23;i<330;i+=23){line(30,i,420,i);//水平辅助线}
}

\qquad注意,我们使用了rectangle()空心矩形函数绘制网格外框架,使用了line函数依次画出了辅助线。

\qquad我们现在的目标就是将h的坐标转换到网格上去,绘制出球心的轨迹,这样似乎并不复杂,只需要对simulation()函数稍加修改即可。

int simulation()
{float dt = 0.01;//仿真间隔10msfloat dy = 230/h0;//单位纵坐标long int N = (long int)(sim_t/dt);//迭代次数float *h=(float*)calloc(N,sizeof(float));//高度float *v=(float*)calloc(N,sizeof(float));//速度(竖直方向)long int i;//迭代变量float process_duty;//进度init_figure();//初始化图像网格setrop2(R2_COPYPEN);//当前颜色//计算步骤h[0]=h0;v[0]=0;for(i=1;i<N;i++){if(h[i-1]>R)//未发生碰撞{v[i]=v[i-1]-9.8*dt;//速度计算}else//发生碰撞,动能损失alpha,速度损失alpha的开方{v[i]=-sqrt(alpha)*v[i-1];	}h[i]=h[i-1]+v[i]*dt;//高度计算process_duty = (i+1)/(float)(N);putpixel(30+(int)(process_duty*390),330-(int)(h[i]*dy),RED);//画点putpixel(X,Y,color*)Sleep(dt*1000);//延时}free(h);free(v);return 0;
}

\qquad这里的新函数putpixel(X,Y,color*)是画像素点的函数,适合刻画不连续或不规则的移动轨迹。
\qquad现在我们只剩下了刻画进度条的函数了,进度条的刷新很明显是应该放在for循环里面的,那么我们采用什么进度条的格式呢?进度条可以有圆形、扇形、长条连续型、长条不连续型等多种,我们这里采用的是环形进度条,将进度数字显示在环中心。请看以下的对simulation()函数改进的代码:

//仿真运行
int simulation()
{char t[3];//百分值的字符char *out_text;//带百分号的百分字符float dt = 0.01;//仿真间隔10msfloat dy = 230/h0;//单位纵坐标long int N = (long int)(sim_t/dt);//迭代次数float *h=(float*)calloc(N,sizeof(float));//高度float *v=(float*)calloc(N,sizeof(float));//速度(竖直方向)long int i;//迭代变量float process_duty;//进度RECT r={370,35,400,65};//百分值显示区域的矩形指针init_figure();//初始化图像网格setrop2(R2_COPYPEN);//当前颜色setfillcolor(WHITE);setlinecolor(WHITE);fillrectangle(354,19,411,81);//覆盖原进度条区域setlinestyle(PS_NULL);//无线条setbkmode(TRANSPARENT);//设置文字填充背景为透明//计算步骤h[0]=h0;v[0]=0;BeginBatchDraw();//开始缓存区for(i=1;i<N;i++){if(h[i-1]>R)//未发生碰撞{v[i]=v[i-1]-9.8*dt;//速度计算}else//发生碰撞,动能损失alpha,速度损失alpha的开方{v[i]=-sqrt(alpha)*v[i-1];	}setfillcolor(WHITE);setlinecolor(WHITE);fillrectangle(354,19,416,81);//覆盖原进度条区域h[i]=h[i-1]+v[i]*dt;//高度计算process_duty = (i+1)/(float)(N);setlinestyle(PS_SOLID);putpixel(30+(int)(process_duty*390),330-(int)(h[i]*dy),RED);setfillcolor(BLUE);setlinestyle(PS_NULL);fillpie(355,20,415,80,0,process_duty*2*PI);setfillcolor(WHITE);fillcircle(385,50,20);sprintf(t,"%d",(int)(process_duty*100.0));//整型转换为字符串out_text = strcat(t,"%");//添加一个百分号drawtext(out_text,&r,DT_CENTER | DT_VCENTER | DT_SINGLELINE);Sleep(dt*1000);FlushBatchDraw();//刷新缓存区}EndBatchDraw();//结束缓存区free(h);free(v);return 0;
}

\qquad这里我们需要多加载一个头文件<string.h>。
\qquad首先需要计算进度条的坐标,把环形进度条区域用白色矩形刷新掉,环形进度条需要一个扇形和圆形的组合,扇形的角度是0~360°。这里我们用到了fillpie(X1,Y1,X2,Y2,start_angle,end_angle),前四个参数为椭圆扇形的外接矩形坐标,后两个参数分别为起始角和终止角(弧度制)。每过一次迭代都重新计算终止角(起始角始终为0),即可起到扇形角度逐渐增长的效果,再用一个白色填充圆覆盖中心部分即可变成环形进度条。
\qquadFlushBatchDraw()函数是刷新缓存区的函数,与BeginBatchDraw()EndBatchDraw()一起使用,如果我们绘图之后不想立即显示,而想批量绘图最后一起刷新画板,用缓存区的方法再合适不过了。

3.完整代码及效果

#include <graphics.h>              // 引用图形库头文件
#include <conio.h>
#include <stdio.h>
#include <windows.h>				//用到了定时函数sleep()
#include <math.h>
#include <string.h>
#define PI 3.1416
int r[3][4]={{30,20,130,60},{170,20,220,60},{260,20,310,60}};//三个按钮的二维数组
float alpha,R,h0,sim_t;//碰撞时的能量损失率,球的半径、初始高度、仿真时间
//按钮判断函数
int button_judge(int x,int y)
{if(x>r[0][0] && x<r[0][2] && y>r[0][1] && y<r[0][3])return 1;if(x>r[1][0] && x<r[1][2] && y>r[1][1] && y<r[1][3])return 2;if(x>r[2][0] && x<r[2][2] && y>r[2][1] && y<r[2][3])return 3;return 0;
}
//初始化图像
void init_figure()
{int i;setrop2(R2_COPYPEN);//当前颜色setlinecolor(BLACK);setlinestyle(PS_SOLID);//实线rectangle(30,100,420,330);//外框线setlinestyle(PS_DOT);//点线for(i=30+39;i<420;i+=39){line(i,100,i,330);//竖直辅助线}for(i=100+23;i<330;i+=23){line(30,i,420,i);//水平辅助线}
}
//仿真运行
int simulation()
{char t[3];//百分值的字符char *out_text;float dt = 0.01;//仿真间隔10msfloat dy = 230/h0;//单位纵坐标long int N = (long int)(sim_t/dt);//迭代次数float *h=(float*)calloc(N,sizeof(float));//高度float *v=(float*)calloc(N,sizeof(float));//速度(竖直方向)long int i;//迭代变量float process_duty;//进度RECT r={370,35,400,65};//百分值显示区域的矩形指针init_figure();//初始化图像网格setrop2(R2_COPYPEN);//当前颜色setfillcolor(WHITE);setlinecolor(WHITE);fillrectangle(354,19,411,81);//覆盖原进度条区域setlinestyle(PS_NULL);//无线条setbkmode(TRANSPARENT);//设置文字填充背景为透明//计算步骤h[0]=h0;v[0]=0;BeginBatchDraw();//开始缓存区for(i=1;i<N;i++){if(h[i-1]>R)//未发生碰撞{v[i]=v[i-1]-9.8*dt;//速度计算}else//发生碰撞,动能损失alpha,速度损失alpha的开方{v[i]=-sqrt(alpha)*v[i-1];	}setfillcolor(WHITE);setlinecolor(WHITE);fillrectangle(354,19,416,81);//覆盖原进度条区域h[i]=h[i-1]+v[i]*dt;//高度计算process_duty = (i+1)/(float)(N);setlinestyle(PS_SOLID);putpixel(30+(int)(process_duty*390),330-(int)(h[i]*dy),RED);setfillcolor(BLUE);setlinestyle(PS_NULL);fillpie(355,20,415,80,0,process_duty*2*PI);setfillcolor(WHITE);fillcircle(385,50,20);sprintf(t,"%d",(int)(process_duty*100.0));//整型转换为字符串out_text = strcat(t,"%");//添加一个百分号drawtext(out_text,&r,DT_CENTER | DT_VCENTER | DT_SINGLELINE);Sleep(dt*1000);FlushBatchDraw();//刷新缓存区}EndBatchDraw();//结束缓存区free(h);free(v);return 0;
}int main()
{int i,event=0;char s[30];//输入字符串变量short win_width,win_height;//定义窗口的宽度和高度win_width = 480;win_height = 360;initgraph(win_width,win_height);//初始化窗口(黑屏)for(i=0;i<256;i+=5){setbkcolor(RGB(i,i,i));//设置背景色,原来默认黑色cleardevice();//清屏(取决于背景色)Sleep(30);//延时30ms}RECT R1={r[0][0],r[0][1],r[0][2],r[0][3]};RECT R2={r[1][0],r[1][1],r[1][2],r[1][3]};RECT R3={r[2][0],r[2][1],r[2][2],r[2][3]};LOGFONT f;//字体样式指针gettextstyle(&f);					//获取字体样式_tcscpy(f.lfFaceName,_T("宋体"));	//设置字体为宋体f.lfQuality = ANTIALIASED_QUALITY;    // 设置输出效果为抗锯齿  settextstyle(&f);                     // 设置字体样式settextcolor(BLACK);				//BLACK在graphic.h头文件里面被定义为黑色的颜色常量drawtext("输入参数",&R1,DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R1内输入文字,水平居中,垂直居中,单行显示drawtext("运行",&R2,DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R2内输入文字,水平居中,垂直居中,单行显示drawtext("退出",&R3,DT_CENTER | DT_VCENTER | DT_SINGLELINE);//在矩形区域R3内输入文字,水平居中,垂直居中,单行显示setlinecolor(BLACK);rectangle(r[0][0],r[0][1],r[0][2],r[0][3]);rectangle(r[1][0],r[1][1],r[1][2],r[1][3]);rectangle(r[2][0],r[2][1],r[2][2],r[2][3]);MOUSEMSG m;//鼠标指针while(true){m = GetMouseMsg();//获取一条鼠标消息switch(m.uMsg){case WM_MOUSEMOVE:setrop2(R2_XORPEN);setlinecolor(LIGHTCYAN);//线条颜色为亮青色setlinestyle(PS_SOLID, 3);//设置画线样式为实现,10磅setfillcolor(WHITE);//填充颜色为白色if(button_judge(m.x,m.y)!=0){if(event != button_judge(m.x,m.y)){event = button_judge(m.x,m.y);//记录这一次触发的按钮fillrectangle(r[event-1][0],r[event-1][1],r[event-1][2],r[event-1][3]);//有框填充矩形(X1,Y1,X2,Y2)}}else{if(event!=0)//上次触发的按钮未被修正为原来的颜色{fillrectangle(r[event-1][0],r[event-1][1],r[event-1][2],r[event-1][3]);//两次同或为原来颜色event = 0;}}break;case WM_LBUTTONDOWN:setrop2(R2_NOTXORPEN);//二元光栅——NOT(屏幕颜色 XOR 当前颜色)for(i=0;i<=10;i++){setlinecolor(RGB(25*i,25*i,25*i));//设置圆颜色circle(m.x,m.y,2*i);Sleep(20);//停顿30mscircle(m.x,m.y,2*i);//抹去刚刚画的圆}//按照按钮判断左键单击后的操作switch(button_judge(m.x,m.y)){//复原按钮原型case 1:InputBox(s,30,"请输入碰撞时的能量损失率、球的半径、初始高度、仿真时间");sscanf(s,"%f%f%f%f",&alpha,&R,&h0,&sim_t);//将输入字符串依次扫描到全局变量里面FlushMouseMsgBuffer();//单击事件后清空鼠标消息break;case 2:simulation();//仿真运行FlushMouseMsgBuffer();//单击事件后清空鼠标消息break;case 3:closegraph();//关闭绘图环境exit(0);//正常退出default:FlushMouseMsgBuffer();//单击事件后清空鼠标消息//printf("\r\n(%d,%d)",m.x,m.y);//打印鼠标坐标,方便调试时确定区域break;}break;}}return 0;
}

在这里插入图片描述
在这里插入图片描述
希望本文对您有帮助,谢谢阅读。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/544463.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【MATLAB】无人驾驶车辆的模型预测控制技术(精简讲解和代码)【运动学轨迹规划】

文章目录<font color#19C>0.友情链接<font color#19C>1.引言<font color#19C>2.预测模型<font color#19C>3.滚动优化<font color#08CF>3.1.线性化3.2.UrU_rUr​的求取<font color#08CF>3.3.离散化与序列化<font color#08CF>3.4.实现…

顶级Javaer,常用的 14 个类库

作者&#xff1a;小姐姐味道&#xff08;微信公众号ID&#xff1a;xjjdog&#xff09;昨天下载下来Java16尝尝鲜。一看&#xff0c;好家伙&#xff0c;足足有176MB大。即使把jmc和jvisualvm给搞了出去&#xff0c;依然还是这么大&#xff0c;真的是让人震惊不已。但即使JDK足够…

单层神经网络线性回归_单层神经网络| 使用Python的线性代数

单层神经网络线性回归A neural network is a powerful tool often utilized in Machine Learning because neural networks are fundamentally very mathematical. We will use our basics of Linear Algebra and NumPy to understand the foundation of Machine Learning usin…

面试官:说一下 final 和 final 的 4 种用法?

作者 | 王磊来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;重要说明&#xff1a;本篇为博主《面试题精选-基础篇》系列中的一篇&#xff0c;查看系列面试文章请关注我。Gitee 开源地址…

面试官:int和Integer有什么区别?为什么要有包装类?

作者 | 磊哥来源 | Java面试真题解析&#xff08;ID&#xff1a;aimianshi666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;重要说明&#xff1a;本篇为博主《面试题精选-基础篇》系列中的一篇&#xff0c;查看系列面试文章请关注我。Gitee 开…

innodb是如何存数据的?yyds

前言如果你使用过mysql数据库&#xff0c;对它的存储引擎&#xff1a;innodb&#xff0c;一定不会感到陌生。众所周知&#xff0c;在mysql5以前&#xff0c;默认的存储引擎是&#xff1a;myslam。但mysql5之后&#xff0c;默认的存储引擎已经变成了&#xff1a;innodb&#xff…

【MATLAB】卡尔曼滤波器的原理及仿真(初学者专用)

文章目录0.引言1.场景预设2.卡尔曼滤波器3.仿真及效果0.引言 \qquad本文参考了Matlab对卡尔曼滤波器的官方教程及帮助文档&#xff08;Kalman Filter&#xff09;。官方教程的B站链接如下&#xff0c;在此对分享资源的Up主表示感谢。(如不能正常播放或需要看中文字幕&#xff0…

Go实现查找目录下(包括子目录)替换文件内容

为什么80%的码农都做不了架构师&#xff1f;>>> 【功能】 按指定的目录查找出文件&#xff0c;如果有子目录&#xff0c;子目录也将进行搜索&#xff0c;将其中的文件内容进行替换。 【缺陷】 1. 没有过滤出文本文件 2. 当文件过大时&#xff0c;效率不高 【代码】…

卡诺模板_无关条件的卡诺地图

卡诺模板Till now, the Boolean expressions which have been discussed by us were completely specified, i.e., for each combination of input variable we have specified a minterm by representing them as 1 in the K-Map. But, there may arise a case when for a giv…

面试官:final、finally、finalize 有什么区别?

作者 | 磊哥来源 | Java面试真题解析&#xff08;ID&#xff1a;aimianshi666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;重要说明&#xff1a;本篇为博主《面试题精选-基础篇》系列中的一篇&#xff0c;查看系列面试文章请关注我。Gitee 开…

【Matlab】扩展卡尔曼滤波器原理及仿真(初学者入门专用)

文章目录0.引言及友情链接1.场景预设2.扩展卡尔曼滤波器3.仿真及效果0.引言及友情链接 \qquad卡尔曼滤波器&#xff08;Kalman Filter, KF&#xff09;是传感器融合&#xff08;Sensor Fusion&#xff09;的基础&#xff0c;虽然知乎、CSDN、GitHub等平台已有大量的学习资料&am…

Windows 8.1 升级到专业版

本例将一台 Windows 8.1 平板升级到专业版。升级前&#xff1a;升级的原因&#xff0c;是因为用户发现这台平板不能启用远程桌面管理。查看计算机属性&#xff0c;显示如下&#xff1a;从上面的信息可以看出&#xff0c;目前这台平板安装的不是专业版。具体是什么版本呢&#x…

【MATLAB】求点到多边形的最短距离

文章目录0.引言1.原理2.代码及实用教程0.引言 \qquad点与多边形的关系无非三种——内部、上、外部。本文定义点在多边形内部距离为负&#xff0c;点在多边形边上距离为0&#xff0c;到多边形外部距离为正。 1.原理 计算点到多边形的距离分为3个步骤&#xff1a; 判断点与多边…

【Python】mmSegmentation语义分割框架教程(自定义数据集、训练设定、数据增强)

文章目录0.mmSegmentation介绍1.mmSegmentation基本框架1.1.mmSegmentation的model设置1.2.mmSegmentation的dataset设置1.2.1.Dataset Class文件配置1.2.2.Dataset Config文件配置1.2.3.Total Config文件配置2.运行代码 3.展示效果图和预测X.附录X.1.mmSegmentation框架解释 X…

面试官:重写 equals 时为什么一定要重写 hashCode?

作者 | 磊哥来源 | Java面试真题解析&#xff08;ID&#xff1a;aimianshi666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;重要说明&#xff1a;本篇为博主《面试题精选-基础篇》系列中的一篇&#xff0c;关注我&#xff0c;查看更多面试题。…

【python】获取PC机公网IP并发送至邮箱

文章目录0.引言1.获取外网IP2.打开SMTP服务3.python发送邮件4.完整代码0.引言 \qquad之前一直使用Putty连接公司的PC机进行远程办公&#xff0c;苦于外网的IP地址不能固定下来&#xff0c;所以购买了内网穿透服务&#xff0c;免费版还会限速。后来转念一想&#xff0c;如果能定…

List 去重的 6 种方法,这个方法最完美!

作者 | 王磊来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;在日常的业务开发中&#xff0c;偶尔会遇到需要将 List 集合中的重复数据去除掉的场景。这个时候可能有同学会问&#xff1a…

Mongodb -(3) replica set+sharding

分片集搭建---何旭东目录分片集搭建...................................................................................................................... 1生态系统...............................................................................................…

electron 菜单栏_如何在Electron JS中添加任务栏图标菜单?

electron 菜单栏If you are new here, please consider checking out my recent articles on Electron JS including Tray Icons. 如果您是新来的&#xff0c;请考虑查看我最近关于Electron JS的文章&#xff0c; 包括托盘图标 。 In this tutorial, we will set up 2 menu it…

【逆强化学习-0】Introduction

文章目录专栏传送门0.引言1.逆强化学习发展历程2.需要准备的专栏传送门 0.简介 1.学徒学习 2.最大熵学习 0.引言 \qquad相比于深度学习&#xff0c;国内强化学习的教程并不是特别多&#xff0c;而相比强化学习&#xff0c;逆强化学习的教程可谓是少之又少。而本人想将整理到的资…