C语言图形化界面 您所在的位置:网站首页 c语言游戏案例 C语言图形化界面

C语言图形化界面

2024-06-30 14:32| 来源: 网络整理| 查看: 265

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

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

#include // 引用图形库头文件 #include #include #include //用到了定时函数sleep() #include int main() { int i; short win_width,win_height;//定义窗口的宽度和高度 win_width = 480;win_height = 360; initgraph(win_width,win_height);//初始化窗口(黑屏) for(i=0;i30,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;ir1[0],r1[1],r1[2],r1[3]};//矩形指针R1 RECT R2={r2[0],r2[1],r2[2],r2[3]};//矩形指针R2 RECT R3={r3[0],r3[1],r3[2],r3[3]};//矩形指针R3 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]); system("pause");//暂停,为了显示 closegraph(); return 0; }

在这里插入图片描述 \qquad 这里需要特别介绍的是矩形指针 p R e c t pRect pRect,它使用句柄RECT定义,并且不可以中途再次赋值。之所以要设置矩形指针了为了打印字体的时候以矩形为边界自动填充。它的格式是RECT r={X1,Y1,X2,Y2},X1和X2是矩形的左边和右边的横坐标,Y1和Y2是矩形的上边和下边的纵坐标,这一点和rectangle()绘制空心矩形函数参数排列一致。后面的DT_CENTER | DT_VCENTER | DT_SINGLELINE就是描述填充格式的常量。使用drawtext书写文字不需要再计算文字的坐标和设置大小,会方便很多。 \qquad LOGFONT是字体样式指针,通过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 // 引用图形库头文件 #include #include #include //用到了定时函数sleep() #include 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;ir1[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{30,20,130,60},{170,20,220,60},{260,20,310,60}};//三个按钮的二维数组 int button_judge(int x,int y) { if(x>r[0][0] && xr[0][1] && yr[1][0] && xr[1][1] && yr[2][0] && xr[2][1] && y 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 float dt = 0.01;//仿真间隔10ms 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;//迭代变量 for(i=1;i 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 line(30,i,420,i);//水平辅助线 } }

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

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

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

3.完整代码及效果 #include // 引用图形库头文件 #include #include #include //用到了定时函数sleep() #include #include #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] && xr[0][1] && yr[1][0] && xr[1][1] && yr[2][0] && xr[2][1] && y line(i,100,i,330);//竖直辅助线 } for(i=100+23;i char t[3];//百分值的字符 char *out_text; float dt = 0.01;//仿真间隔10ms float 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 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;ir[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 //复原按钮原型 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; }

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



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有