设计目标
1、把商品信息存放到文件中
2、程序启动时加载文件信息到数据结构中
3、根据用户选择的功能显示商店或购物车信息
4、根据用户点击的商品添加、删除商店或购物车内容
功能描述
1、侧边功能栏可以浏览商品以及购物车
2、浏览商品时选中商品自动加入购物车,若数量为0则不加入,在购物车点击商品就减少该商品的数量,减至0则移除购物车
3、购物车中显示商品信息,带有翻页功能以及统计总价
4、结账功能会在屏幕显示购物信息,并且保存到文本文件,商店信息也重写到文本,最后退出程序。
设计方案
设计一个哈希表存储商店信息,一条链表存储购物车信息。
链表节点结构体包括数据域与指针域,数据域结构体有商品类型、商品名、数量、价格、图片路径。指针域使用内核链表。
设计一个函数解码jpg图片并且映射到lcd
设计字库,在商品图片下面显示商品信息,在左下角提示用户的操作
循环读取用户点击位置以触发相应功能
实现过程
首先读取商店信息的文件到哈希表中,fsanf格式化输入一行一行读取文件到结构体,根据数据结构体中的类别插入到哈希表。初始化LCD,把硬件设备层/dev/fb0映射到用户层,然后显示背景。这里用的图片都是jpg格式,要先对jpg进行解码,然后把24位的jpg图片转为32位才可以在屏幕中显示,最后把转化完的argb信息映射到lcd屏幕。
打开触摸屏/dev/input/event0,循环读取用户点击的位置,如果用户点击的是商店类型,show_list遍历该种类型的哈希表,读取结构体中的商品信息,解码映射jpg图片,并在图片下方使用字库显示商品信息,在进入一个循环读入用户是否要购物,点击右侧功能栏会退出这个循环,在这个循环中,如果用户点击了商品,就会加入购物车,商品数量-1,重新显示商品信息,商品数量为0则提示添加失败。如果用户点击购物车,就会遍历购物车链表,解码映射图片,字库显示信息。点击的位置是图片就购物车数量-1,并且商店的数量会+1,如果数量为0就不会显示在购物车。在此界面设计有翻页,商品的种类超过一页,就可以翻页,第二页内容全部删除则会跳转到前一页。
最后用户点击结账就退出读取用户点击的位置的循环,利用字库显示购物信息,并且保存到文本中,关闭打开的字库、触摸屏结束进程。
主函数
#include "head.h"
float TOTAL_COST = 0;
int GOODS_NUM = 0;
//int (*lcd)[800]=NULL;
int main()
{
//write_shop.c写商店信息
//1 加载商店信息到链表
//初始化哈希表
struct node *hash[3]; //0.饮料, 1.水果 2.零食
init_hash(hash);
//初始化购物车
struct list_head *cart=malloc(sizeof(struct list_head));
INIT_LIST_HEAD(cart);
//加载文件中的数据到哈希表中
load_shop(hash);
//show_hash(hash);//显示哈希表
//初始化LCD
init_lcd();
//显示背景
read_JPEG_file("picture/1-background.jpg",0,0);
//加载字库
font *f = fontLoad("/usr/share/fonts/DroidSansFallback.ttf");
if(f == NULL)puts("加载字库失败");
//提示框
msg_tips(f,"欢迎使用XX商店系统");
//打开触摸屏,在read_screen中打开
// int fd = open("/dev/input/event0",O_RDWR);
// if(fd<0){puts("Failed to open screen");return -1;}
//读输入设备的值**************************************************
read_screen(f,hash,cart);
//关闭触摸屏在read_screen中操作
//close(fd);
//开发板显示购物车进行结账
puts("n购物信息:");
show_cart(cart);
printf("商品数量 %d ,共消费 %.2f 元n",GOODS_NUM,TOTAL_COST);
//显示屏幕信息
display_from_cart_to_screen(cart,f);
//保存小票
save_ticket(cart);
//关闭字库
fontUnload(f);
//回写商店
save_shop(hash);
}
//arm-linux-gcc main.c 1-数据结构.c 2-LCD.c 3-map_jpeg.c -o main -L/home/gec/jpeglib/lib -ljpeg -L./ -lm -lfont
1-数据结构.c
#include "head.h"
//#include "list.h"
int GOODS_TYPE_NUM = 0;
//初始化哈希表
void init_hash(struct node *hash[3])
{
for(int i=0;i<3;i++)
{
hash[i] = malloc(sizeof(struct node));
INIT_LIST_HEAD(&hash[i]->list);
}
}
//加载文件
void load_shop(struct node *hash[3])
{
//打开文件
FILE *fp = fopen("shop.txt","r");
if(fp == NULL){puts("open shop.txt fail");return;}
//不断读取文本信息
while (1)
{
//数据结构体
struct shop data;
int ret = fscanf(fp,"%d %s %d %f %sn",&data.type,data.name,&data.num,&data.price,data.pic_path);
if(ret <= 0){printf("读取完毕n");return;}
//输出商品信息
//printf("%d %s %d %.2f %sn",data.type,data.name,data.num,data.price,data.pic_path);
//根据类别插入哈希表
struct node *xnew = malloc(sizeof(struct node));
xnew->data = data;
//插入
list_add_tail(&xnew->list,&hash[data.type]->list);
}
}
//遍历测试
void show_hash(struct node *hash[3])
{
for(int i=0;i<3;i++)
{
struct node *pos = hash[i];
list_for_each_entry(pos,&hash[i]->list,list)
{
printf("%d %s %d %.2f %sn",pos->data.type,pos->data.name,pos->data.num,pos->data.price,pos->data.pic_path);
}
}
}
//查找节点
struct node *find_node(struct list_head *head,char *name)
{
struct node *pos = NULL;
list_for_each_entry(pos,head,list)
{
if(strcmp(pos->data.name,name)==0)
{
printf("购物车中已有 %s,",pos->data.name);
return pos;
}
}
return NULL;
}
//购买商品
struct shop *buy_goods(struct node *hash[3],int type,int n,struct list_head *cart)
{
//int type,n;//商品类型,序号
//根据序号找到商品
struct list_head *p = &hash[type]->list;
p = p->next;//指向头的下一个
for(int i=0;inext;
//取出商品
struct node *msg = list_entry(p,struct node,list);
if(msg->data.num <= 0 )
{
printf("加入购物车失败n");
return NULL;
}else
{//加入购物车
msg->data.num--;
add_to_cart(cart,msg->data);
printf("%s 加入购物车成功n",msg->data.name);
return &msg->data; //返回该商品
}
}
//加入购物车
void add_to_cart(struct list_head *head,struct shop data)
{
struct node *xnew = find_node(head,data.name);
if(xnew == NULL)//第一次加入
{
struct node *xnew = malloc(sizeof(struct node));
xnew->data = data;
xnew->data.num=1;
list_add_tail(&xnew->list,head);
GOODS_TYPE_NUM++;
}
else//多次加入购物车
{
xnew->data.num++;
}
TOTAL_COST += data.price;
}
//遍历购物车
void show_cart(struct list_head *head)
{
struct node *pos=NULL;
list_for_each_entry(pos,head,list)
{
printf("%d %s %d %.2f %sn",pos->data.type,pos->data.name,pos->data.num,pos->data.price,pos->data.pic_path);
}
}
//取消买某物
void del_form_cart(struct list_head *head,int n,struct node *hash[3])
{
struct list_head *p=head->next;
if(p==NULL)return;
for(int i=0;inext;//p指向序号n,找到要删除的位置
if(p==NULL)return;
//找到大节点
struct node *pos = list_entry(p,struct node,list);
printf("删除%sn",pos->data.name);
//商店该商品数量加1
struct node *shop_node=NULL;
int type = pos->data.type;//商品类型
list_for_each_entry(shop_node,&hash[type]->list,list)
{
if(strcmp(shop_node->data.name,pos->data.name)==0)
{
shop_node->data.num++;
break;
}
}
TOTAL_COST -= pos->data.price;
//处理购物车 (因为可能要释放pos,所以这一步要放到最后)
if(pos->data.num==1)
{
list_del(p);//删除链表
free(pos);//释放大结构体堆空间
GOODS_TYPE_NUM--;
}
else if (pos->data.num>1)
{
pos->data.num--;
}
}
//遍历一条链
void show_list(struct list_head *head,font *f)
{
struct node *pos = NULL;
//画图位置
int i=20,j=20,j1=180,num=1;
list_for_each_entry(pos,head,list)
{
//printf("%d %s %d %.2f %sn",pos->data.type,pos->data.name,pos->data.num,pos->data.price,pos->data.pic_path);
//显示图片
read_JPEG_file(pos->data.pic_path,i,j);
//显示信息
char msg[1024]={0};
sprintf(msg,"%sn数量%d 价格%.2f",pos->data.name,pos->data.num,pos->data.price);
display_product_information(f,i,j1,msg);
i+=165;num++;
if(num>4){i=20;j=240;j1=400;num=1;}
}
}
//可视化购物车
void show_cart_list(struct list_head *head,font *f,int page)
{
//struct node *pos = NULL;
struct node *pos = list_entry((head)->next, typeof(*pos), list);
for(int i=0;ilist.next, typeof(*pos), list);
}//定位到具体页码开始
//画图位置
int i=20,j=20,j1=180,num=1,temp=1;
//list_for_each_entry(pos,head,list)
for (pos; &pos->list != (head); pos = list_entry(pos->list.next, typeof(*pos), list))
{
printf("%d %s %d %.2f %sn",pos->data.type,pos->data.name,pos->data.num,pos->data.price,pos->data.pic_path);
//显示图片
read_JPEG_file(pos->data.pic_path,i,j);
//显示信息
char msg[1024]={0};
sprintf(msg,"%s 数量:%dn单价%.2f",pos->data.name,pos->data.num,pos->data.price);
display_product_information(f,i,j1,msg);
i+=165;num++,temp++;
if(num>4){i=20;j=240;j1=400;num=1;}
if(temp>8) break;
}
}
void save_ticket(struct list_head *head)
{
FILE *fp = fopen("ticket.txt","w+");//重写
if(fp==NULL){puts("open ticket.txt fail");return;}
fprintf(fp,"超市小票n");
//遍历购物车
struct node *pos=NULL;
list_for_each_entry(pos,head,list)
{
//printf("%d %s %d %.2f %sn",pos->data.type,pos->data.name,pos->data.num,pos->data.price,pos->data.pic_path);
fprintf(fp,"%s %.2f %dn",pos->data.name,pos->data.price,pos->data.num);
}
fprintf(fp,"总消费:%.2f元n",TOTAL_COST);
fclose(fp);
}
void save_shop(struct node *hash[3])
{
FILE *fp = fopen("shop.txt","w+");//重写
for(int i=0;i<3;i++)
{
struct node *pos=NULL;
list_for_each_entry(pos,&hash[i]->list,list)//遍历一条链
{
fprintf(fp,"%d %s %d %.2f %sn",pos->data.type,pos->data.name,pos->data.num,pos->data.price,pos->data.pic_path);
}
}
fclose(fp);
}
2-LCD.c
#include "head.h" // #include "font.h" // #includeint (*lcd)[800]=NULL; //初始化LCD void init_lcd() { int lcd_fd = open("/dev/fb0",O_RDWR); if(lcd_fd<0){puts("open lcd fail");return;} lcd = mmap(NULL,800*480*4,PROT_READ|PROT_WRITE,MAP_SHARED,lcd_fd,0); close(lcd_fd); } //显示商品信息 void display_product_information(font *f,int x,int y,char *msg) { // font *f = fontLoad("/usr/share/fonts/DroidSansFallback.ttf"); // if(f == NULL)puts("加载字库失败"); //2.设置字体的大小 fontSetSize(f,18); //3.设置字体输出框的大小 bitmap *b = createBitmap(145,40, 4); //4.把字体输出到输出框中 fontPrint(f,b,0,0, msg,0x0000ff00, 0);//颜色RGBA //5.把输出框的所有信息显示到LCD屏幕中 unsigned int *p = (void *)lcd; show_font_to_lcd(p,x,y,b); // 关闭字体库,不需要频繁关闭 //fontUnload(f); // 关闭bitmap destroyBitmap(b); } //提醒框 void msg_tips(font *f,char *msg) { fontSetSize(f,18); bitmap *b = createBitmap(350,20, 4); //4.把字体输出到输出框中 fontPrint(f,b,0,0, msg,0xff000000, 0);//颜色RGBA //5.把输出框的所有信息显示到LCD屏幕中 unsigned int *p = (void *)lcd; show_font_to_lcd(p,0,460,b); destroyBitmap(b); } //获取序号 int get_seq_num() { int fd = open("/dev/input/event0",O_RDWR); if(fd<0){puts("Failed to open screen");return -1;} int x=0,y=0,t=0; while(1) { struct input_event ts; read(fd,&ts,sizeof(struct input_event)); if(ts.type == EV_ABS && ts.code == ABS_X){x = ts.value*0.78125;}//printf("x=%d ",x); if(ts.type == EV_ABS && ts.code == ABS_Y){y=ts.value*0.8;}//printf("y=%dn",y); if(ts.type == EV_KEY && ts.code == BTN_TOUCH){t=ts.value;}//printf("t=%dn",t); //printf("x=%d y=%d t=%dn",x,y,t); if(20 700&&y>386&&t==1)return -2;//结账 if(550 430&&t==1)return -3;//向左 if(640 430&&t==1)return -4;//向右翻页 else if(x>700&&t==1) // ||((y>450&&x>565)) { //puts("退出读取界面序号"); return -1; t=0; } } } void display_from_cart_to_screen(struct list_head *head,font *f) { //显示背景 read_JPEG_file("picture/2-bg.jpg",0,0); unsigned int *p = (void *)lcd; fontSetSize(f,26); char msg[2048]="超市小票n"; bitmap *b = createBitmap(300,400, 4); //遍历购物车 struct node *pos=NULL; list_for_each_entry(pos,head,list) { printf("%d %s %d %.2f %sn",pos->data.type,pos->data.name,pos->data.num,pos->data.price,pos->data.pic_path); sprintf(msg,"%sn%s %.2f x%d",msg,pos->data.name,pos->data.price,pos->data.num); } sprintf(msg,"%snn共消费 %.2f 元",msg,TOTAL_COST); //4.把字体输出到输出框中 fontPrint(f,b,40,20, msg,0x00000000, 0);//颜色RGBA //5.把输出框的所有信息显示到LCD屏幕中 show_font_to_lcd(p,200,40,b); destroyBitmap(b); char buf[200]={0}; sprintf(buf,"商品数量 %d ,共消费 %.2f 元,完整小票已保存到ticket.txtn",GOODS_NUM,TOTAL_COST); msg_tips(f,buf); } //读屏幕 参数 ,字库,哈希表,购物车 int read_screen(font *f,struct node *hash[3],struct list_head *cart) { //打开触摸屏 int fd = open("/dev/input/event0",O_RDWR); if(fd<0){puts("Failed to open screen");return -1;} int x_value=0,y_value=0,touch=0; while(1) { struct input_event ts; read(fd,&ts,sizeof(struct input_event)); if(ts.type == EV_ABS && ts.code == ABS_X){x_value = ts.value*0.78125;} if(ts.type == EV_ABS && ts.code == ABS_Y){y_value=ts.value*0.8;} if(ts.type == EV_KEY && ts.code == BTN_TOUCH){touch=ts.value;} //饮料 if(x_value>700&&35 list,f); //获取序号 int seq=1; while(seq!=-1)//读取当前界面购买某物 { seq = get_seq_num();//失败-1 //printf("seq=%dn",seq); if(seq==-1) { read_JPEG_file("picture/1-background.jpg",0,0); msg_tips(f,"点击右侧功能以继续"); break; } else if(seq>=0) {//加入购物车 struct shop *s=NULL; s = buy_goods(hash,key,seq,cart);//类型,序号 if(s==NULL){msg_tips(f,"加入购物车失败,商品缺货");} else{char msg[50]={0};sprintf(msg,"%s 加入购物车成功!",s->name);msg_tips(f,msg);show_list(&hash[key]->list,f);GOODS_NUM++;} } } x_value=0,y_value=0,touch=0; } //水果 if(x_value>700&&110 700 && 186 700&&310 0) { puts("向左翻页"); cart_page--; read_JPEG_file("picture/2-bg.jpg",0,0); show_cart_list(cart,f,cart_page); //消息框 //char buf[50]={0}; memset(buf,0,100); //sprintf(buf,"当前浏览第%d页,共%d页",cart_page+1,(GOODS_TYPE_NUM-1)/8+1); sprintf(buf,"数量 %d,产生消费 %.2f元,第%d页/共%d页n",GOODS_NUM,TOTAL_COST,cart_page+1,((GOODS_TYPE_NUM-1)/8)+1); msg_tips(f,buf); } } else if(seq == -4)//向右翻页 { if(GOODS_NUM>0 && (cart_page<(GOODS_TYPE_NUM-1)/8)) { puts("向右翻页"); cart_page++; read_JPEG_file("picture/2-bg.jpg",0,0); show_cart_list(cart,f,cart_page); //消息框 //char buf[50]={0}; memset(buf,0,100); sprintf(buf,"数量 %d,产生消费 %.2f元,第%d页/共%d页n",GOODS_NUM,TOTAL_COST,cart_page+1,((GOODS_TYPE_NUM-1)/8)+1); msg_tips(f,buf); } } //删除某物 else if(seq>=0) {//移除购物车 seq += cart_page*8;//具体页码的序号 if(seq>=GOODS_TYPE_NUM)continue; del_form_cart(cart,seq,hash);//seq购物车中的类型序号 GOODS_NUM--; //显示背景 read_JPEG_file("picture/2-bg.jpg",0,0);//清除残留 if(GOODS_NUM>0&&(cart_page>(GOODS_TYPE_NUM-1)/8))//减一页 cart_page--; //显示购物车信息 memset(buf,0,100); sprintf(buf,"数量 %d,产生消费 %.2f元,第%d页/共%d页n",GOODS_NUM,TOTAL_COST,cart_page+1,((GOODS_TYPE_NUM-1)/8)+1); show_cart_list(cart,f,cart_page);msg_tips(f,buf); } } if(seq==-2)break;//结账 x_value=0,y_value=0,touch=0; } }//循环读取屏幕 close(fd); }
3-map-jpeg.c
#include#include "/home/gec/jpeglib/include/jpeglib.h" #include #include #include"head.h" //int (*lcd)[800]; struct my_error_mgr { struct jpeg_error_mgr pub; jmp_buf setjmp_buffer; }; typedef struct my_error_mgr * my_error_ptr; METHODDEF(void) my_error_exit (j_common_ptr cinfo) { my_error_ptr myerr = (my_error_ptr) cinfo->err; (*cinfo->err->output_message) (cinfo); longjmp(myerr->setjmp_buffer, 1); } GLOBAL(int) read_JPEG_file (char * filename,int sx,int sy) { struct jpeg_decompress_struct cinfo; struct my_error_mgr jerr; FILE * infile; JSAMPARRAY buffer; int row_stride; if ((infile = fopen(filename, "rb")) == NULL) { fprintf(stderr, "can't open %sn", filename); return 0; } cinfo.err = jpeg_std_error(&jerr.pub); jerr.pub.error_exit = my_error_exit; if (setjmp(jerr.setjmp_buffer)) { jpeg_destroy_decompress(&cinfo); fclose(infile); return 0; } jpeg_create_decompress(&cinfo); jpeg_stdio_src(&cinfo, infile); (void) jpeg_read_header(&cinfo, TRUE); (void) jpeg_start_decompress(&cinfo); row_stride = cinfo.output_width * cinfo.output_components; buffer = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1); //重点 //打印图片信息 //printf("width=%d height=%d bit=%dn",cinfo.output_width,cinfo.output_height,cinfo.output_components); if(cinfo.output_width>800||cinfo.output_height>480){puts("图片太大");return 0;} //cinfo.output_width=800;cinfo.output_height=480; char argb[cinfo.output_height][cinfo.output_width*4]; int y=0; while (cinfo.output_scanline < cinfo.output_height) { (void) jpeg_read_scanlines(&cinfo, buffer, 1); //put_scanline_someplace(buffer[0], row_stride); //转32位 for(int x=0;x 写商店信息,
write_shop.c
#include#include #include #include #include #include //商店结构体 struct shop{ int type; char name[50]; int num; float price; char pic_path[1024]; }; void save_shop(int type,char *name,int num,float price,char* path) { //打开商店文件 FILE *fp = fopen("shop.txt","a+"); if(fp==NULL){puts("open shop.txt fail");return;} //写文件 fprintf(fp,"%d %s %d %.2f %sn",type,name,num,price,path); fclose(fp); } void load_shop() { FILE *fp = fopen("shop.txt","r"); while(1) { // char name[50],path[1024]; // int num,type; // float price; struct shop s; int ret = fscanf(fp,"%d %s %d %f %sn",&s.type,s.name,&s.num,&s.price,s.pic_path); if(ret == -1)break;//读取完毕 printf("%d %s %d %.2f %sn",s.type,s.name,s.num,s.price,s.pic_path); } fclose(fp); } int main() { save_shop(0,"可乐",100,2.5,"picture/kele.jpg"); save_shop(0,"芬达",1,3.5,"picture/fenda.jpg"); save_shop(0,"王老吉",56,5,"picture/wanglaoji.jpg"); save_shop(0,"加多宝",57,5,"picture/jiaduobao.jpg"); save_shop(0,"牛奶",83,3.5,"picture/niunai.jpg"); save_shop(0,"冰红茶",1,4,"picture/binghongcha.jpg"); save_shop(0,"阿萨姆",95,4,"picture/asamu.jpg"); save_shop(0,"雪碧",27,3,"picture/xuebi.jpg"); save_shop(1,"苹果",40,6.4,"picture/apple.jpg"); save_shop(1,"香蕉",36,13.8,"picture/banana.jpg"); save_shop(1,"凤梨",52,39,"picture/fengli.jpg"); save_shop(1,"奇异果",38,48.5,"picture/qiyiguo.jpg"); save_shop(1,"火龙果",42,37,"picture/huolongguo.jpg"); save_shop(1,"牛油果",49,52,"picture/niuyouguo.jpg"); save_shop(1,"葡萄",52,68.8,"picture/putao.jpg"); save_shop(1,"橙子",35,34.5,"picture/chengzi.jpg"); save_shop(2,"薯片",38,22.6,"picture/shupian.jpg"); save_shop(2,"铜锣烧",49,16.9,"picture/tongluoshao.jpg"); save_shop(2,"奥利奥",21,19.9,"picture/aoliao.jpg"); save_shop(2,"辣条",39,13.5,"picture/latiao.jpg"); save_shop(2,"饼干",49,43,"picture/binggan.jpg"); save_shop(2,"好丽友",32,35,"picture/haoliyou.jpg"); save_shop(2,"蛋卷",44,65,"picture/danjuan.jpg"); save_shop(2,"巧克力",29,36.5,"picture/qiaokeli.jpg"); load_shop(); }



