《图形和用户界面技术.ppt》由会员分享,可在线阅读,更多相关《图形和用户界面技术.ppt(49页珍藏版)》请在三一办公上搜索。
1、第13章图形和用户界面技术,本 章 导 读 界面是程序的一个窗口,是用户与程序之间的桥梁。友善的用户界面,不仅可以使用户感觉赏心悦目、使用方便、自然有趣,更能提高程序的使用效率。因此,用户界面是应用程序不可缺少的组成部分,而用户界面分为文本形和图两种。本章将学习图形显示技术和菜单设计,并为编辑器添加菜单,完善用户界面。本章的教学目标:掌握图形显示技术;了解汉字显示方法;掌握文本方式和方式下的菜单设计;为编辑器添加菜单,完善用户界面,完成编辑器的最终设计目标。,第13章图形和用户界面技术,13.1 文本窗口的程序设计 13.2 文本下拉式菜单设计 13.3 图形显示技术 13.4 汉字显示技术
2、13.5 图形方式下的菜单设计 13.6 菜单的综合应用,13.1 文本窗口的程序设计,13.1.1 窗口定义 Turbo C使用函数window()定义屏幕窗口,函数原型为:void window(int x1,int y1,int x2,int y2);参数x1,y1是窗口左上角的坐标,x2,y2是右下角的坐标。并规定整个屏幕左上角的坐标为(1,1),右下角的坐标为(80,25)。例如,定义一个窗口,它的左上角在屏幕的第5列第10行处,宽为30列,高15行,可调用函数:window(5,10,35,25);,13.1.2 窗口操作函数,窗口设计举例,【例13.1】创建一个宽64列,高17行
3、的窗口。窗口左上角坐标是(7,7),背景颜色为白色,字符为蓝色。然后在窗口正中间显示若干字符,如图13-1 所示,图13-1 创建窗口,#include conio.hmain()char name80;textbackground(WHITE);/*设置白色背景颜色*/clrscr();/*清屏,使设置的背景颜色生效*/window(7,7,71,24);textbackground(5);/*设置5号背景颜色*/textcolor(BLUE);/*设置蓝色文本颜色*/clrscr();/*清屏,使设置的颜色生效*/gotoxy(16,8);cprintf(One window is cre
4、ated!);getch();window(1,1,80,25);/*恢复全屏幕窗口*/textbackground(BLACK);/*恢复0号背景颜色*/textcolor(7);/*恢复7号前景颜色*/clrscr();/*清屏,使设置的颜色生效*/,13.2 文本下拉式菜单设计,13.2.1 设计目标 主菜单分为:File、Edit、Search和Option四项。其中:File项包含5个子菜单:New,Open,Save,Save As,Exit;Edit项包含6个子菜单:Cut,Copy,Paste,Del,Selected All,Edit;Search项包含2个子菜单:Find,
5、Replace;Option项包含4个子菜单:Undo,Redo,Config,Help;(1)启动menu,进入程序主菜单,利用左、右(、)键,可操作主菜单间左右移动;(2)利用上、下(、)键,可控制子菜单间上下移动;(3)回车确认光带所选菜单项;(4)选择菜单File/Exit,退出系统。,13.2.2 菜单设计方法,编写下拉式菜单的一般步骤:保存屏幕弹出前的矩形区域;显示菜单正文;产生光条;读取按键信息;根据菜单项的选择,处理相应的事务;恢复屏幕弹出前的原始状态;连编成一个完整的程序。,1 保存和恢复屏幕区域,保存屏幕可用gettext()函数,其函数原型为:int gettext(in
6、t x1,int y1,int x2,int y2,void*buffer);将屏幕上以(x1,y1)为左上角、(x2,y2)右下角的矩形区域的文本内容存入buffer指针指向的内存空间。内存所用字节数为:(y2-y1+1)X(x2-x1+1);恢复保存的屏幕内容可调用函数puttext()完成,其函数原型为:int puttext(int x1,int y1,int x2,int y2,void*buffer);将gettext()函数存入内存buffer中的文字内容拷贝到屏幕指定位置,2.显示菜单正文,利用光标定位函数gotoxy()和字符串显示函数cputs()就可完成。void dis
7、p_menu_item(int x,int y,int fcolor,int bcolor char*string)textbackground(bcolor);textcolor(fcolor);gotoxy(x,y)cputs(string);函数的功能是在屏幕(x,y)点,用背景颜色bcolor、字符颜色fcolor显示字符串指针string所指向的字符串。3 产生光条 最简单的方法是用不同的背景颜色和字符颜色重写光条的菜单项,调用函数disp_menu_item()完成。,4读取按键信息,菜单设计中最关键的一点是功能键的读取。而键盘上有两种键:ASCII码键和特殊功能键,为通用起见,我
8、们编写了如下GetKey()函数,用来返回键值,当读取的键是普遍键时,返回其ASCII码值;如是特殊功能键时,返回大于256的整数。int GetKey(void)union unsigned int x;unsigned char y2;key1;while(bioskey(1)=0);key1.x=bioskey(0);return(key1.y0=0?(key1.y1+256):key1.y0);,13.2.3 程序框架及功能函数,1主程序框架void main(void)InitScreen();/*初始化屏幕*/WMainMenu();/*显示主菜单*/WSubMenu();/*显示
9、子菜单*/SelectMenu();/*根据用户按键,处理相事务*/Quit();/*恢复屏幕*/,2菜单的框架函数(SelectMenu),约定:左、右移动键是主菜单间左右移动选择,由SlectMainMenu()函数实现;而上、下键是子菜单间上下移动选择,由SlectSubMenu()函数实现;回车键进入事务处理子函数SubFunGo(),由菜单项File/exit退出,其中:Mm是主菜单项变量,SmmMm是对应第Mm项菜单的子菜单项变量,LEFT、RIGHT、UPPER、DOWN是对应于左右、上下移动键的符号常量,注意与GetKey()函数返回值一致。void SelectMenu(vo
10、id)while(1)Key=GetKey();/*循环接受键盘输入*/if(Key=LEFT|Key=RIGHT)SlectMainMenu();/*主菜单左右选择*/if(Key=UPPER|Key=DOWN)SlectSubMenu();/*子菜单上下选择*/if(Key=ENTER)/*回车确认选项*/if(!SubFunGo()break;/*执行编辑命令操作*/,3.主菜单选择函数,void SlectMainMenu(void)window(1,1,80,25);/*恢复主菜单光条*/disp_menu_item(SbXMm,1,14,3,MainMm);/*恢复屏幕*/putt
11、ext(SbXMm-2,2,SbXMm+SbWidMm+1,SbNumMm+4,buf);if(Key=LEFT)Mm=Mm=0?3:Mm-1;/*实现主菜单的循环*/if(Key=RIGHT)Mm=Mm=3?0:Mm+1;disp_menu_item(SbXMm,1,14,4,MainMm);/*设置光条*/WSubMenu();/*显示下拉式菜单*/,4.子菜单选择函数,void SlectSubMenu(void)/*子菜单项的选择*/*光条恢复*/disp_menu_item(2,1+SmmMm,15,3,SubMmSmmMm);if(Key=UPPER)/*实现子菜单项的循环*/Sm
12、mMm=SmmMm=0?SbNumMm-1:SmmMm-1;if(Key=DOWN)SmmMm=SmmMm=SbNumMm-1?0:SmmMm+1;/*设置光条*/disp_menu_item(2,1+SmmMm,14,1,SubMmSmmMm);,13.3 图形显示技术,13.3.1 图形系统控制 1 图形系统的初始化用函数initgraph完成图形系统的初始化,函数原形为:void far initgraph(int far*gdriver,int far*gmode,char far*path)其中:gdriver为图形驱动程序,参见表13-2,在graphics.h中有相应的枚举常量,
13、常用的有DETECT(自动检测)、EGA、VGA。gmode为图形适配器的工作模式,graphics.h中常用的枚举常量有EGAHI、VGAHI。path为查找图形驱动程序所用的路径名。2 图形系统的关闭图形系统处理完毕后,需调用closegraph函数关闭图形系统,退出图形状态,函数原形为:void far closegraph(void),13.3.2 基本图形函数,Turbo C提供了丰富的图形函数,一些基本的图形可经过直接调用图形函数而得到。对于一般的图形都可以由它们组合而成。基本的图形函数均包含在头文件graphics.h中,如下表13-3所示。,表13-3 基本图形函数,13.3.
14、3 时钟程序,/*包含文件*/time_t ltime;/*类型time_t即long*/struct tm*pt;/*pt为指向时间结构tm的指针*/void showTime(void)/*以图形方式显示运转的时钟*/int Rs=100;/*秒针长度*/int Rc=130;/*时钟外圆圈半径*/double alpha=2*PI/60.0;/*秒针1秒钟和分针1分钟所旋转的弧度数*/int x,y;/*时钟圆心坐标*/int xSecond,ySecond;/*秒针针尖位置坐标*/int driver=DETECT,mode;initgraph(/*画时钟外圆圆圈*/,while(!k
15、bhit()/*按下任一键则退出循环*/time(/*调用图形方式显示函数*/,13.4 汉字显示技术,13.4.1 16点阵字模 字库中的汉字按1616点阵模式存储,每个汉字由1616=256个点组成,占用162=32个字节单元。字节的每一位(bit)表示一个点的属性(1有亮点,0无亮点),连续的两个字节表示该汉字字模的一行,32个字节的排列序如图13-5所示。,13.4.2 汉字的内码,在计算机内英文字符是用一个字节的ASCII代码表示,一般用七位表示128个字符,而最高位作为奇偶校验(或不用)。我国国标规定汉字用内码表示,内码为两个字节。为了与西文兼容,汉字系统的内码必须同时允许ASCI
16、I码和汉字内码的使用,两者之间不应冲突,所以规定每个字节只用7位(bit),若两个字节的最高位均为1,则该字符为汉字。,13.4.3 字模的显示,汉字库分成若干区,每个区有94个汉字,每个汉字均有一个确定的区码和位码,知道了区位码也就相当于知道了汉字在字库中的位置,由于汉字的内码与区位码有一定的关系,所以只要通过汉字的内码就可以得到汉字的区位码,从而可以获得该汉字的字模,然后在图形方式下,读取字模的每一个字节的每一位即可按画点的方式在屏幕上显示出汉字。由于在中文DOS下,输入汉字时,其相应的内码已在程序中存在,即内码已在汉字的位置,如同在西文DOS下,输入英文字符时,其对应的ASCII码也在程
17、序中存在一样,设某个汉字内码为十六进制数aaff,则区号qh和位号wh分别为:qh=aa 0 xa0;wh=ff 0 xa0;则该汉字在汉字库中的起始位置为(以字节为单位):offset=(94*(qh-1)+(wh-1)*32L;,连续读取32个字节就是该汉字的字模。对每个字节的每一位进行判断,如果是1就画点,否则就不画,这样就可以显示汉字来。因此,由汉字内码,从汉字库中读取字模的可用下函数实现:void get_hzm(char incode,char bytes)unsigned char qh,wh;unsigned long offset;qh=incode0 0 xa0;/*得到区
18、号*/wh=incode1 0 xa0;/*得到位号*/offset=(94*(qh-1)+(wh-1)*32L;/*得到偏移位置*/fseek(fp,offset,SEEK_SET);/*定位字模首字节*/fread(bytes,1,32,fp);,void disp_hzm(int x,int y,int color,char buf)/*显示字模的函数*/int i,j,k;for(i=0;i(7-k),【例13.4】在例13.3的三维直方图中添加汉字“三维直方图(中国深圳)”字样,如图13-6所示。分析:只要在例13.3的程序中添加get_hzm(),disp_hzm()和DisHz1
19、6X16()子函数,并在适当的位置插入打开汉字库、关闭文件程序段和调用函数DisHz16X16()的语句。程序清单如下(省略了未变动的语句和前面给出的函数):main()FILE*fp;./*初始化*/fp=fopen(“hzk16.dot”,”rb”);/*打开16点阵汉字库文件hzk16.dot*/.Bar3DDemo();DisHz16X16(100,10,4,12,”三维直方图(中国深圳)”);fclose(fp);getch();closegraph();,汉字显示的应用举例,1.对于24X24点阵的汉字库,如何修改显示程序DisHz16X16()?2.修改显示函数DisHz16X1
20、6(),将其放大两倍,课堂练习及思考,图13-6三维直方图,13.5.1 屏幕的保存和恢复 void far getimage(int x1,int y1,int x2,int y2,void far*mapbuf);void far putimage(int x1,int y1,void far*mapbuf,int op);unsigned far imagesize(int x1,int y1,int x2,int y2);在利用getimage()函数保存屏幕时,必需用imagesize()计算被保存的左上角(x1,y1),右下角(x2,y2)的图形屏幕区域的全部内容所需要的字节数,然
21、后再給mapbuf分配一个所求得的字节数的内存空间。调用putimage()函数,将保存在mapbuf指向的内存中的图象输出到左上角为(x1,y1)的位置上,其中,putimage()函数中的参数op规定如何把内存中的图象与对应屏幕运算方式,详见表13-4。,13.5 图形方式下的菜单设计,【例13.6】利用getimage()和putimage()函数,显示一个沿sin()函数曲线运动的圆。void main(void)int gdrive=DETECT,gmode=VGAHI,i;void*buff=NULL;initgraph(/*清屏*/,屏幕的保存和恢复举例,do for(i=0;i
22、640;i+)delay(1000);/*延时*/*从内存复制图像显示到内存*/putimage(30+i,200+sin(i/60.0)*60.0,buff,COPY_PUT);while(!kbhit();/*反复显示运动过程直至按键*/if(buff!=NULL)free(buff);/*释放动态申请内存空间*/closegraph();/*退出图形系统*/,在图形方式下,显示菜单项的区域由左上角和右下角坐标确定,因此,我们必须将文本方式下的菜单项的显示函数:void disp_menu_item(int x,int y,int fcolor,int bcolor char*string
23、)textbackground(bcolor);textcolor(fcolor);gotoxy(x,y)cputs(string);修改为:void disp_menu_item(int x1,int y1,int x2,int y2,int fc,int bc,char*item)setfillstyle(1,bc);bar(x1,y1,x2,y2);DisHz16X16(x1+10,y1+4,bcolor,fc,item);,13.5.2 菜单项的显示函数,凡是调用了disp_menu_item()、gettext()和puttext()的相关函数也必须作相应改动,诸如:WmainMen
24、u()、WsubMenu()、SlectMainMenu(void)、SlectSubMenu(void)。修改后的代码请读者参考教材p292-293.,13.5.3 其它相关函数的修改,13.5.4 修改Wind()和InitScreen()函数,void InitScreen()setfillstyle(1,1);bar3d(0,0,639,479,0,0);setfillstyle(1,3);bar3d(0,0,639,25,0,0);bar3d(0,430,639,450,0,0);setfillstyle(1,6);bar3d(0,450,639,479,0,0);setcolor(
25、15);outtextxy(10,436,edithelp);outtextxy(10,460,mainhelp);,void Wind(int x1,int y1,int x2,int y2,int FrmTp,int BCl,int TCl)setfillstyle(1,BCl);bar(x1,y1,x2,y2);setcolor(TCl);if(FrmTp)rectangle(x1+2,y1+2,x2-2,y2-2);if(FrmTp=2)rectangle(x1+4,y1+4,x2-4,y2-4);,void InitGr(void)/*添加图形初始化*/int GraphDrive=
26、DETECT,GraphMode=VGAHI;detectgraph(,13.5.5 添加InitGr()和Quit()函数,将原来以字符为单位定位变量的初始化语句:int SbWid4=10,10,10,10;int SbX4=5,15,23,32;改为以象素定位:int SbWid4=93,93,93,90;int SbX4=20,110,200,290;将西文菜单文本修改为中文:char*Main4=文件,编辑,查找,操作;char*Sub46=“新文件”,打开,保存,另存为,退出,裁剪,复制,粘贴,删除,”全选”,编辑,查找,替换,取消,恢复,设置,”帮助”;,13.5.6 修改部分变
27、量的初始化,为程序tedit6添加文本下拉菜单,实现第6章(图6-9)提出的设计目标。因此,下面将进一步优化tedit6,将13.2中编写的下拉式菜单程序框架emenu.c与程序tedit6.c整合,升级编辑器第七版tedit7。13.6.1 编辑器第七版tedit7 程序tedit7功能结构如图13-8所示。主菜单分为:File、Edit、Search和Option四项。,13.6 菜单的综合应用,图13-8 程序tedit7功能结构图,程序tedit7的开发,就是把程序tedit6.c与文本下拉式菜单程序框架emenu.c溶合在一起,在emenu.c的功能菜单中调用tedit6.c的功能函
28、数。但不是简单的嵌套调用,因为它们的所有操作是在各自特定的窗口中进行的。所以,我们要设法在它们之间建立关联。分4步完成:(1)修改tedit6.c的屏幕最大显示字符行和列,使宽、高与emenu.c的可编辑文本窗口一致。以及程序中与之相关的语句,并取消功能操作提示help()函数。(2)改造部分功能函数,如字符串查找和替换函数(mysearch,myreplace),因为,在编辑状态下,需要提示信息和输入信息。将tedit6.c中的main函数改为edit编辑功能函数,注意提取初始化init函数、以及修改屏幕滚动函数。(3)以菜单程序框架为主函数,调用编辑器的功能函数。保留原操作方式,添加菜单选
29、取方式。(4)按照多程序文件连编规则,重新整理、规范tedit6.c和emenu.c。把函数说明、全局变量加上static属性予以保护,以免同名造成混淆;而公用的变量或函数在其中之一程序文件中定义,在另一程序文件中说明是外部extern属性。,13.6.2 程序tedit7的开发,1.修改屏幕最大显示行和列 程序menu.c的窗口是设置在全屏幕(即25行,80列),除去主菜单、状态提示栏,可编辑窗口只是中间部分(20行,78列)。因此,该区域的大小应该为修改后tedit7的编辑屏幕区域大小。而程序tedit6.c是设置在全屏幕(即25行,80列),编辑文本窗口由宏定义MAX_LINES,LIN
30、E_LEN确定。你可以改变宏定义值,实现编辑文本窗口尺寸的变化。考虑到取消功能操作提示行和四周边框,因此,将tedit6.c中的宏定义:#define LINE_LEN 80#define MAX_LENS 24修改为:#define LINE_LEN 78#define MAX_LENS 20,13.6.3 程序tedit6的改造,在字符串查找myserach()和替换myreplace()、文件存储save()和加载load()函数中,我们应将用户的输入在状态栏中进行,所以需要进行窗口的切换,为此,先定义两个窗口的切换函数:(1)从编辑窗口切换到状态栏void goto_propmt(vo
31、id)window(1,25,80,25);/*切换到状态栏窗口*/clrline(1);gotoxy(20,1);textattr(0 x6e);(2)从状态栏恢复到编辑窗口void goto_edit(void)gotoxy(1,1);cputs(mainhelp);/*恢复提示*/window(2,3,79,22);/*恢复到编辑窗口*/textbackground(0);textcolor(7);,2.修改部分功能函数,在myserach()、myreplace()、save()和load()四个函数中,在提示用户输入时,将原来的光标定位一律用goto_prompt()替换;反之,完成
32、输入后,返回到编辑状态之前,则用goto_edit()替换。例如,在myserach()中,粗斜体语句是替换或添加的,省去了未改变的部分。其它三个函数留给读者完成。,void mysearch(void)char str80,*p;int len,i,k,j;goto_prompt();/*切换到状态栏窗口*/printf(input search string:);edit_gets(str);if(!str0)gotox_edit();/*恢复到编辑窗口*/gotoxy(point.scrnx,point.scrny);return;,.if(!*p)printf(not find);go
33、tox_edit();/*恢复到编辑窗口*/gotoxy(point.scrnx,point.scrny);return;.gotox_edit();/*恢复到编辑窗口*/gotoxy(point.scrnx,point.scrny);,(3)修改滚动函数因边框,需将上、下滚动函数scrollup()、scrolldn()中的语句 r.h.ch=topy-1;r.h.cl=topx-1;r.h.dh=endy-1;r.h.dl=endx-1;替换为r.h.ch=topy-1+2;r.h.cl=topx-1+1;r.h.dh=endy-1+2;r.h.dl=endx-1+1;,(4)修改tedi
34、t6.c中的main函数为编辑功能子函数edit(),以被主程序调用。移出初始化函数init()。void edit(void)union unsigned int i;char ch2;key;do/*循环接受用户输入*/while(bioskey(1)=0);/*检测键盘直到有击键才结跳出循环*/key.i=bioskey(0);/*读取键盘扫描码*/./*未改变*/while(key.ch0!=ESC);,(5)相关函数调用的修改把程序tedit6.c 中初始化函数init()移到menu.c的主函数中。main()Screen();WMainMenu();init();WSubMenu
35、();SelectMenu();Quit();return 0;,1主程序框架void main(void)InitScreen();/*初始化;WMainMenu();/*显示主菜单*/init();WSubMenu();/*显示子菜单*/SelectMenu();/*菜单操作*/Quit();/*恢复文本模式;*/void SelectMenu(void)while(1)Key=GetKey();/*循环接受键盘输入*/if(Key=LEFT|Key=RIGHT)SlectMainMenu();/*主菜单左右选择*/if(Key=UPPER|Key=DOWN)SlectSubMenu();
36、/*子菜单上下选择*/if(Key=ENTER)/*回车确认选项*/SubFunGo();/*执行编辑命令操作*/,13.6.4 程序框架及功能函数,(1)菜单程序与编辑器窗口的切换 程序tedit6.c的窗口应定位于程序menu.c的可编辑文本窗口内。调用窗口创建函数window(2,3,79,22),就可把编辑器定位在menu.c的可编辑文本窗口内。修改函数SelectMenu,利用窗口切换技术,实现文本编辑操作与功能菜单选择的切换。请参考教材p299函数:void SelectMenu(void),2.功能函数,(2)从菜单程序调用编辑器的功能函数例如,从菜单项File/Open 调用文
37、件加载、存储、另存为函数,其它类推。请参考教材p299-300函数:int SubFunGo(),1.多程序文件连编我们把修改后的tedit6.c重新命名为tedit7.c,menu.c重新命名为tedit8.c。多程序文件连编有两种方法:(1)文件包含法 用“文件包含”是一种最简单的方法,例如,对于本程序,只要在tedit8.c的头部用:#include“tedit7.c”这种方法等价于一个程序文件,相单于把tedit7.c的代码嵌套到tedit8.c中。(2)工程文件法 建立一个工程文件是最常用的方法,尤其对于多人合作开发较大项目。其工程文件内容由程序文件名组成,例如,设工程文件取名为te
38、dit.prj,其内容由两行组成,即tedit7.ctedit8.c,13.6.5 多文件程序的连编,对于不同程序文件间的函数、公共变量的说明必须遵循以下原则:(1)全局的公共变量,只能在一个文件中定义,在需要用到的文件中加以说明为外部的:extern 变量类型 变量名;(2)需要调用另一程序文件中定义的函数时,也必须说明是外部的,一般建议在文件头部说明。如:extern 函数类型 函数名(形参类型列表序列);(3)为避免同名造成混淆,对于非公用的全局变量或函数定义为静态的存储属性,即:static 变量类型 变量名;static 函数类型 函数名(形参类型列表序列);详细内容请参考“函数与变
39、量“一章。,工程文件法注意事项,(1)修改程序tedit7.c存储属性重新整理、规范tedit7.c。把局限于本程序的变量、函数,在前面加上静态属性关键字static。而要引用程序文件tedit8.c中的变量则说明为外部的属性关键字extern。完整的程序代码见附录D 的tedit7.c。(2)修改程序tedit8.c存储属性同样,重新整理、规范tedit8.c。把局限于本程序的变量、函数,在前面加上静态属性关键字static。而要引用程序文件tedit7.c中的变量则说明为外部的属性关键字extern。完整的程序代码见附录V 的tedit8.c。(3)创建工程文件工程文件内容由程序文件名组成,如,设工程文件取名为tedit.prj,其内容由两行组成,即tedit7.ctedit8.c 设置编译、连接的工程文件tedit.prj(Projiect/Projiect name);(4)编译、连接,生存可执行的编辑器程序。,2文本编辑器的连编,请不用工程文件方法,直接用包含文件,修改、编译、调试程序tedit.c。,思考题,