基于GEC6818开发板的点餐系统(c++封装实现)

项目显示视频地址:【基于GEC6818开发板的餐厅点餐系统项目演示】 https://www.bilibili.com/video/BV1aC4y1V79Q/?share_source=copy_web&vd_source=4d0afc24dfef0496a61c04a2665aed83


前言

这是基于GEC6818开发板实现的点餐系统,是个人结合老师的上课内容自行制作,其中封装好的类和库有一些并不是我自己全部写出来的,更多的是在老师的指导下完成的,作为本人第一篇csdn文章,所以分享出来供大家学习讨论。


一、封装各种项目所需要的类

因为该项目涉及到很多功能类,所以在做这个项目之前,我就已经把需要用到的类全部封装好了,比如说线程类,字库类,lcd类,touch触摸屏类,bmp类等等,做这个项目的目的是巩固c++所学的知识,熟悉c++的三大特性,封装、继承、多态,在这个项目中,会大量使用到c++的封装和继承,所以很适合拿来巩固所学。

 一些封装好的类如下:thread.cpp(线程类)

#include "thread.h"


void*task(void*arg) //void*arg = this
{
    //th 基类的指针 指向的是 派生类对象
    Thread*th = (Thread*)arg;
    //通过基类的指针 调用的是 派生类对象中的虚函数
    th->run();
}


Thread::Thread()
{   
    //这个标记位用来停止线程  
    isExit = false;
}

void Thread::start()//void start(TimeThread*this)
{
    pthread_create(&th,NULL,task,this);
    pthread_detach(th);
}

void Thread::stop()
{
    isExit = true;
}

thread.h

#ifndef THREAD_H
#define THREAD_H

#include <pthread.h>
#include <unistd.h>

void*task(void*arg);

//设计一个线程基类,规范 所有的线程 启动线程 停止线程都用统一的接口
class Thread
{
public:
    Thread();

    //启动线程
    void start();

    //停止线程
    void stop();
    //线程要执行的任务,把该虚函数声明为纯虚函数
    virtual void run() = 0;

protected:
    pthread_t th;
    bool isExit;
};

#endif // THREAD_H

bmp.cpp类

#include "bmp.h"


Bmp::Bmp(const char *fileName, int *lcd_p)
{
    strcpy(m_fileName,fileName);
    m_mp = lcd_p;

    //获取文件的属性
    getInfo();
}

Bmp::~Bmp()
{

}

void Bmp::getInfo()
{
    //2、打开bmp图片文件
    int bmpfd = open(m_fileName,O_RDONLY);
    if(bmpfd == -1)
    {
        perror("open bmpfile error");//perror将错误的原因打印出来
        return ;
    }

    //读取bmp图片的文件头 + 信息头数据
    BITMAPFILEHEADER fileHead;
    BITMAPINFOHEADER infoHead;

    read(bmpfd,&fileHead,14);//读取14个字节
    read(bmpfd,&infoHead,40);//读取40个字节

    m_width = infoHead.biWidth;
    m_height = infoHead.biHeight;

    std::cout<<"m_width:"<<m_width<<"m_height:"<<m_height<<std::endl;

    close(bmpfd);
}

void Bmp::setFileName(const char *fileName)
{
    memset(m_fileName,0,sizeof(m_fileName));
    strcpy(m_fileName,fileName);

    //获取文件的属性
    getInfo();
}

void Bmp::setFileName(char *fileName)
{
    memset(m_fileName,0,sizeof(m_fileName));
    strcpy(m_fileName,fileName);

    //获取文件的属性
    getInfo();
}

void Bmp::show(int x,int y)
{
    char bmpbuf[m_width*m_height*3];
    int tempbuf[m_width*m_height];
    int i,j;

    int bmpfd = open(m_fileName,O_RDONLY);
    if(bmpfd == -1)
    {
        perror("open bmp failed");
        return ;
    }
    lseek(bmpfd,54,SEEK_SET);
    read(bmpfd,bmpbuf,m_width*m_height*3);
    //颜色值顺序  BGR--ARGB
    for(i=0,j=0; i<m_width*m_height; i++,j+=3)
    {
        tempbuf[i] = 0x00<<24|bmpbuf[j+2]<<16|bmpbuf[j+1]<<8|bmpbuf[j]<<0;
    }
    //上下颠倒
    for(j=0; j<m_height; j++)
    {
        for(i=0; i<m_width; i++)
        {
            m_mp[(j+y)*800+i+x] = tempbuf[(m_height-1-j)*m_width+i];
        }
    }

    close(bmpfd);
}

bmp.h

#ifndef BMP_H
#define BMP_H

#include <iostream>

#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/mman.h>
#include <stdbool.h>

#include "lcd.h"

#define UINT  unsigned short
#define DWORD unsigned  int
#define LONG unsigned  int
#define WORD unsigned short

#define BMP_WIDTH     800
#define BMP_HEIGHT     480


class Bmp
{
    //前面14个字节 文件信息头
    typedef struct tagBITMAPFILEHEADER{
        UINT bfType;  //2
        DWORD bfSize; //DWORD 4
        UINT bfReserved1; //2
        UINT bfReserved2; //2
        DWORD bfOffBits; //DWORD 4
    }BITMAPFILEHEADER;

    //40个字节 位图信息头
    typedef struct tagBITMAPINFOHEADER{ /* bmih */
        DWORD biSize; //DWORD 4
        LONG biWidth; //4  图像的宽度
        LONG biHeight;//4  图像的高度
        WORD biPlanes; //2
        WORD biBitCount;//2
        DWORD biCompression; //DWORD 4
        DWORD biSizeImage; //DWORD 4
        LONG biXPelsPerMeter;//4
        LONG biYPelsPerMeter;//4
        DWORD biClrUsed; //DWORD 4
        DWORD biClrImportant; //DWORD 4
    }BITMAPINFOHEADER;


public:
    Bmp(const char*fileName,int*lcd_p=Lcd::instance()->addr());
    ~Bmp();

    void getInfo();
    void setFileName(const char*fileName);
    void setFileName(char*fileName);

    void show(int x=0,int y=0);

private:
    char m_fileName[256];
    int m_width;
    int m_height;
    int *m_mp;
};

#endif // BMP_H

为了避免文章篇幅过长,剩下的类就不一一展示 

二、总体思路

        在做项目之前,首先我们得先把项目的总体框架想好,既然需要的模块都已经准备好了,那就得会用起来才行,c++是一门面向对象的语言,我们需要把项目的实现分界成一个个对象,上面封装好的类就是一个个对象了,我们现在就需要使用接口把这些对象联系起来。 

        既然我们的项目是多个界面的,那就得用到多线程,不如一个界面就对应一个线程,所以我们首先需要弄一个界面基类,那就是widge,让这个类继承thread线程基类,后面我们设计各个界面的时候再继承widg类,然后重写thread类的run函数,就可以实现每个界面执行不同的任务,也就是每个不同的界面会有不同的菜品啊什么的,点击的位置也不一样,具体看你怎么设计。

1.设计一个界面基类

widget.cpp代码如下(示例):

#include "widget.h"

Widget::Widget()
{
    m_bgName = "./res/bg.bmp";
}

void Widget::run()
{
    std::cout<<" Widget::run()"<<std::endl;
    while(1)
    {

        Bmp b1(m_bgName.data());
        b1.show();

        Touch touch;
        int x,y;
        touch.wait(x,y);

    }
}

void Widget::show()
{
    this->start();//启动线程
}

void Widget::exec()
{
    this->run();
}

void Widget::setStyleSheet(std::string style)
{
    m_bgName = style;
}

wedget.h

#ifndef WIDGET_H
#define WIDGET_H

#include "bmp.h"
#include "touch.h"

#include "thread.h"

#include <string>
#include <iostream>

//界面基类
class Widget:public Thread
{
public:
    Widget();

    void run() override;

    void show();//显示接口 - 线程运行 --界面不会阻塞
    void exec();//显示接口---界面会阻塞
    void hide();//隐藏

    void setStyleSheet(std::string style);//设置界面的背景图片
protected:
    std::string m_bgName;// 界面的背景图片
};

#endif // WIDGET_H

2.让自己写的界面类继承界面基类

比如我有一个界面是专门卖饮料的,那这个界面就需要继承widg

比如drink.cpp

#include "drink.h"


Drink::Drink()
{
    m_bgName = "./res/4.bmp";
}

void Drink::run()
{
    std::cout<<" Drink::run()"<<std::endl;
    while(1)
    {
        int state = 0;
        Bmp b1(m_bgName.data());
        b1.show();

        MFont* mk1 = MFont::instance();
        std::string result = std::to_string(Shopnum::chengzhi);
        //将string类型的数据转化为const char* 类型的数据,然后用字库进行显示
        const char* ret1 = result.c_str();
        mk1->show(ret1,24,20,20,-1,445,90,0,0,-16777216);


        MFont* mk2 = MFont::instance();
        std::string result2 = std::to_string(Shopnum::guozhi);
        //将string类型的数据转化为const char* 类型的数据,然后用字库进行显示
        const char* ret2 = result2.c_str();  
        mk2->show(ret2,24,20,20,-1,510,90,0,0,-16777216);     

        MFont* mk3 = MFont::instance();
        std::string result3 = std::to_string(Shopnum::maidong);
        //将string类型的数据转化为const char* 类型的数据,然后用字库进行显示
        const char* ret3 = result3.c_str();
        mk1->show(ret3,24,20,20,-1,445,295,0,0,-16777216);


        MFont* mk4 = MFont::instance();
        std::string result4 = std::to_string(Shopnum::pijiu);
        //将string类型的数据转化为const char* 类型的数据,然后用字库进行显示
        const char* ret4 = result4.c_str();  
        mk2->show(ret4,24,20,20,-1,510,295,0,0,-16777216); 

        Touch touch;
        int x,y;
        touch.wait(x,y);

        if(x>235 && x<270  && y>235 && y<265)
        {
            //爆浆鸡爪饭加一
            state = 1;
        }
        else if(x>440 && x<470  && y>225 && y<265)
        {
            //爆浆鸡爪减一
            state = 2;
        }
        else if(x>500 && x<530  && y>230 && y<260)
        {
            //爆香大龙虾加一
            state = 3;
        }
        else if(x>700 && x<740  && y>230 && y<260)
        {
            //爆香大龙虾减一
            state = 4;
        }
        else if(x>235 && x<270  && y>435 && y<480)
        {
            //麻辣牛蛙加一
            state = 5;
        }
        else if(x>440 && x<470  && y>435 && y<480)
        {
            //麻辣牛蛙减一
            state = 6;
        }
        else if(x>500 && x<530  && y>435 && y<480)
        {
            //爆炒猪耳朵加一
            state = 7;
        }
        else if(x>700 && x<740  && y>435 && y<480)
        {
            //爆炒猪耳朵减一
            state = 8;
        }
        else if(x>35 && x<170  && y>35 && y<95)
        {
            //热销
            state = 9;
        }
        else if(x>35 && x<170  && y>125 && y<185)
        {
            //主食
            state = 10;
        }
        else if(x>35 && x<170  && y>215 && y<275)
        {
            //小菜
            state = 11;
        }
        else if(x>35 && x<170  && y>305 && y<365)
        {
            //饮料
            state = 12;
        }
        else if(x>35 && x<170  && y>395 && y<455)
        {
            //购物车
            state = 13;
            
        }

        switch (state) {
            case 1:
                std::cout<<"海克斯橙汁加一"<<std::endl;           
                Shopnum::chengzhi++;
                std::cout << "海克斯橙汁数量:"<<Shopnum::chengzhi << std::endl;

                break;

            case 2:
            {
                std::cout<<"海克斯橙汁减一"<<std::endl;
                if(Shopnum::chengzhi >= 1)
                {
                    Shopnum::chengzhi--;
                }           
                
                std::cout << "海克斯橙汁数量:"<<Shopnum::chengzhi << std::endl;

                break;
            }
 
            
            
            case 3:
                std::cout<<"海克斯果汁加一"<<std::endl;           
                Shopnum::guozhi++;
                std::cout << "海克斯果汁数量:"<<Shopnum::guozhi << std::endl;

                break;

            case 4:
            {
                std::cout<<"海克斯果汁减一"<<std::endl;
                if(Shopnum::guozhi >= 1)
                {
                    Shopnum::guozhi--;
                }           
                
                std::cout << "海克斯果汁数量:"<<Shopnum::guozhi << std::endl;

                break; 
            }


            case 5:
                std::cout<<"海克斯脉动加一"<<std::endl;           
                Shopnum::maidong++;
                std::cout << "海克斯脉动数量:"<<Shopnum::maidong << std::endl;

                break;

            case 6:
            {
                std::cout<<"海克斯脉动减一"<<std::endl; 
                if(Shopnum::maidong >= 1)
                {
                    Shopnum::maidong--;
                }          
                
                std::cout << "海克斯脉动数量:"<<Shopnum::maidong << std::endl;

                break;
            }
 

            case 7:
                std::cout<<"海克斯啤酒加一"<<std::endl;           
                Shopnum::pijiu++;
                std::cout << "海克斯啤酒数量:"<<Shopnum::pijiu << std::endl;

                break;

            case 8:
            {
                std::cout<<"海克斯啤酒减一"<<std::endl;  
                if(Shopnum::pijiu >= 1)
                {
                    Shopnum::pijiu--;
                }         
                
                std::cout << "海克斯啤酒数量:"<<Shopnum::pijiu << std::endl;

                break; 
            }



            case 9:{
                Hot h;
                h.exec();
                break;
            }

            case 10:{
                Zhushi z;
                z.exec();
                break;
            }

            
            case 11:{
                Xiaocai x;
                x.exec();
                break;
            }
                

            case 12:{

                break;
            }

            case 13:{
                Shoping s;
                s.exec();
                break;
            }

                       
        }
    }
}

drink.h

#ifndef DRINK_H
#define DRINK_H


#include "widget.h"
#include "zhushi.h"
#include "photowidget.h"
#include "xiaocai.h"
#include "shoping.h"
#include "hot.h"
#include "shopnum.h"

//继承Widget
class Drink:public Widget
{
public:
    Drink();

    void run() override;
};

#endif // DRINK_H

别的界面类都是一个道理,这里就不需要进行一一列举。

3.设计一个应用程序管理类

这个类其实就是main.cpp里面的主线程,这个类对象可以什么都不干,但是需要设置一个循环阻塞住主线程,不然main函数里面的主线程退出之后,子线程(也就是界面类)也会跟着退出。

application.cpp

#include "application.h"

Application::Application()
{

}

int Application::exec()
{
    int data = 0;
    while(1){
        ::sleep(3);
        //std::cout<<"parent pthread:"<<data++<<std::endl;
    }
}

上面的exec()会持续不断的睡眠,以达到阻塞的目的。 

main.cpp

#include <iostream>

#include "application.h"
#include "mainwidget.h"
#include "shopnum.h"

using namespace std;


int main()
{   

    Application app;//实例化一个应用程序管理类的对象 --主线程

    //实例化一个主界面类的对象 ---子线程   UI线程
    MainWidget w;
    //显示
    w.show();

    return app.exec();
}


总结

        总体来说整个项目并不难,特别是你把一些功能模块都封装好了以后,要做什么项目需要用到就可以直接拉过来用,并不需要重新编写,相比与c,代码的复用性大大提高,一开始我也觉得这个项目很难,也在各个网站上找别人做的项目来参考,后面发现多数项目的代码都很臃肿,因为很多都是用c语言编写的。后面我决定自己做了,后面在老师的指导下,按照老师给的框架去弄这个项目,弄好了之后发现确实不难,是一个很合适的练习题目,用来熟悉c++语言相当不错。这是我写的第一篇文章,希望不是最后一篇文章,我也想做一个代码大佬,可惜现在还是个小白,那就一步一步开始吧,在这记录我的学习历程,跟码友们一起进步!

项目源代码 提取:

链接:https://pan.baidu.com/s/1MEHaJOfiNy1EYSxPOWaBKw 
提取码:zlw6 

tcp服务器端源码提取:

 链接:https://pan.baidu.com/s/1IygXzR32iRZQP2mUFXJsTw 
提取码:zlw6

项目所有东西打包提取

链接:https://pan.baidu.com/s/1vKmyumfmjp8z5bW6IvhuSw 
提取码:zlw6

  • 11
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 17
    评论
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值