前言

此文档仅做记录并且分享自己学习的过程,学艺不精,code新人,如有错误欢迎在评论区讨论、指正! 若文章内容对你有帮助,希望可以给我点个赞!
在这里插入图片描述

1.项目背景介绍

  • 为什么想做这个项目: 出于对现实生活中“扫码点单”这一行为背后的实现感到好奇,尝试独立完成的练手项目。
  • 想要做到什么程度: 基本贴合现实生活,不过暂时只能用于小店,因为数据库在本地;包含必要的功能——订单查询,历史查询,门店流水统计,订单跟踪。
  • 项目结果: 项目满分100分的话,大概能给自己打个70分吧,基础功能有一些,但和实际生活中的还差的远;像是历史记录查询、门店营销额度查询没来得及做,界面比较直观了,但还是不太方便。
  • 项目展示:QT练手项目:智能门店

2. 项目讲解

2.1 项目架构

  • 服务器:店主使用;用于查看订单信息和客户需求。
  • 客户端:客户使用;用于扫码点单,提出需求。
  • 消息类型:发送订单、呼叫服务员、结账
  • 消息体:
    • 订单:OREDR 1号 XXX(菜目数量,价格) 总计 XXX 元 END
    • 呼叫:CALL 1号 END
    • 结账:PAY 1号 END

2.2 项目组件及相关代码

  • 服务端:
    开始界面
    在这里插入图片描述
    相关代码:widget.cpp
Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    slog = new serverLogin();//登录界面

    /*----------------设置背景图片------------------*/
    QPixmap pixmap(":/icon/jiemian.jpg");
    QPixmap pix = pixmap.scaled(this->size());//图片自适应窗口
    // 创建调色板
    QPalette palette;
    palette.setBrush(QPalette::Background,QBrush(pix));
    // 应用调色板到窗口
    this->setPalette(palette);
}

Widget::~Widget()
{
    delete ui;
}
// 此处点击开始按钮后,会唤起第二个界面,此处代码处理唤起界面,还支持界面跟随
void Widget::on_pushButton_clicked()
{
    // 获取当前主窗体的位置和大小
    QRect geom = this->geometry();

    // 计算新窗体的位置,使其居中于主窗体
    int x = geom.x() + (geom.width() - slog->width()) / 2;
    int y = geom.y() + (geom.height() - slog->height()) / 2;

    slog->move(x, y); // 移动到计算出的中心位置
    slog->show();//打开登录界面
    this->close();
}

登录界面
在这里插入图片描述
相关代码: serverlogin.cpp涉及到 Sqlite3 数据库,所以代码会稍长一点,代码大致就是从界面上的两个lineEdit 分别的获取账号和密码,注册检查数据库是否存在该用户,若不存在则添加;登录查看数据库是否存在该用户,存在则唤起后续界面。

#include "serverlogin.h"
#include "ui_serverlogin.h"

serverLogin::serverLogin(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::serverLogin)
{
    ui->setupUi(this);
    QPixmap pixmap(":/icon/jiemian.jpg");
    QPixmap pix = pixmap.scaled(this->size());
    // 创建调色板
    QPalette palette;
    palette.setBrush(QPalette::Background,QBrush(pix));
    // 应用调色板到窗口
    this->setPalette(palette);

    menu = new MenuServer();
    msgBox = new QMessageBox(this);

    /*--------------------SQL start-------------------*/
    //创建并打开数据库
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");  //添加数据库驱动
    db.setDatabaseName("my.db");   //设置数据库名
    if (!db.open()) {  //打开数据库
        qDebug() << "Database open failed!";
    }

    query = new QSqlQuery(db);
    QString sql = "create table if not exists server(uid text primary key, passwd text)";
    if (!query->exec(sql)) {    //执行sql语句
        qDebug() << query->lastError().text();   //获取最后一次错误信息
    }
    /*--------------------SQL end-------------------*/
}

serverLogin::~serverLogin()
{
    delete ui;
}

//注册
void serverLogin::on_pushButton_2_clicked()
{
    QString name = ui->lineEdit->text();    //账号
    QString pswd = ui->lineEdit_2->text();  // 密码
    if (name.isEmpty() || pswd.isEmpty()) {
        //QMessageBox::warning(this, "注册用户", "账号与密码不能为空!");
        msgBox->setIcon(QMessageBox::Warning);
        msgBox->setText("注册用户");
        msgBox->setInformativeText("账号与密码不能为空!");
        msgBox->show();
        return;
    }
    QString sql = QString("insert into server values('%1', '%2')").arg(name).arg(pswd);
    if (!query->exec(sql)) {    //执行sql语句
        msgBox->setIcon(QMessageBox::Warning);
        msgBox->setText("注册用户");
        msgBox->setInformativeText(QString("注册失败: %1").arg(query->lastError().text()));
        msgBox->show();
    } else{
        msgBox->setText("注册用户");
        msgBox->setInformativeText("注册成功!");
        msgBox->show();
    }
}

//登录
void serverLogin::on_pushButton_clicked()
{
    QString name = ui->lineEdit->text();    //账号
    QString pswd = ui->lineEdit_2->text();  // 密码
    if (name.isEmpty() || pswd.isEmpty()) {
        msgBox->setIcon(QMessageBox::Warning);
        msgBox->setText("用户登录");
        msgBox->setInformativeText("账号与密码不能为空!");
        msgBox->show();
        return;
    }
    //搜寻用户信息
    QString sql = QString("select * from server where uid='%1'and passwd='%2'").arg(name).arg(pswd);

    if (query->exec(sql)) {
        // 检查是否有匹配的记录
        if (query->next()) {
/* -------------------------登录成功,进入管理界面-------------------------*/
            msgBox->setText("登录成功");
            msgBox->setInformativeText("欢迎使用管理员:" + name + "!");
            msgBox->show();
            // 获取当前主窗体的位置和大小
            QRect geom = this->geometry();
            // 计算新窗体的位置,使其居中于主窗体
            int x = geom.x() + (geom.width() - menu->width()) / 2;
            int y = geom.y() + (geom.height() - menu->height()) / 2;
            this->close();
            menu->move(x, y);
            menu->show();//打开管理界面
        } else {
            // 登录失败,显示失败消息框
            msgBox->setIcon(QMessageBox::Warning);
            msgBox->setText("登录失败");
            msgBox->setInformativeText("用户名或密码错误,请重试");
            msgBox->show();
        }
    } else {
        // 查询执行失败,显示错误信息
        msgBox->setIcon(QMessageBox::Warning);
        msgBox->setText("查询错误");
        msgBox->setInformativeText("数据库查询失败: " + query->lastError().text());
        msgBox->show();
    }
}

服务器订单界面
在这里插入图片描述
相关代码:menuserver.cpp涉及到 TCP 和消息的解析,是服务器最核心的代码部分。

#include "menuserver.h"
#include "ui_menuserver.h"

MenuServer::MenuServer(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::MenuServer)
{
    ui->setupUi(this);

    QPixmap pixmap(":/icon/jiemian.jpg");
    QPixmap pix = pixmap.scaled(this->size());
    // 创建调色板
    QPalette palette;
    palette.setBrush(QPalette::Background,QBrush(pix));
    // 应用调色板到窗口
    this->setPalette(palette);

    msgBox = new QMessageBox(this);
    ui->tabWidget->setCurrentIndex(0);
    this->setWindowTitle("餐厅管理系统");

    /*--------------------------网络相关-----------------------------*/
    server = new QTcpServer;  //1. 创建套接字
    if (!server->listen(QHostAddress::AnyIPv4, 9999)) { //2. bind+listen
        qDebug() << server->errorString();
    }
    //当有新的连接进来时触发
    connect(server, &QTcpServer::newConnection, this, &MenuServer::accept_client);

}

MenuServer::~MenuServer()
{
    delete ui;
}

//接收客户端连接,识别消息类型,根据消息类型做不同的事情
void MenuServer::accept_client(){
    if (server->hasPendingConnections()) { //判断是否有等待的连接
        socket = server->nextPendingConnection();  //接收连接
        msgBox->setText("提示");
        msgBox->setInformativeText("准备迎客!");
        msgBox->show();

        connect(socket, &QTcpSocket::disconnected, [&](){//客户端断开
            msgBox->setText("提示");
            msgBox->setInformativeText("用户离开");
            msgBox->show();
            socket->close();
        });

        /*--------识别消息类型,根据消息类型做不同的事情--------*/
        connect(socket, &QTcpSocket::readyRead, [&](){
            static QByteArray buffer;
            buffer.append(socket->readAll());

            // 检查是否包含 'END' 分隔符以处理完整消息
            int endIndex = buffer.indexOf("END");
            QByteArray messageBytes = buffer.left(endIndex); // 获取完整消息(不包括 'END')

            // 移除已处理的部分(包括 'END' 的长度)
            buffer.remove(0, endIndex + 3);

            QString message = QString::fromUtf8(messageBytes).trimmed(); // 转换为 QString,并去除多余空格

            // 分析消息内容
            QStringList parts = message.split(" ", QString::SkipEmptyParts);

            if (parts.size() > 2 && parts[0] == "ORDER") { // 确保有足够的部分并且是 ORDER 消息
                QString orderType = parts[0]; // "ORDER"
                QString tablenum = parts[1];   // 桌号

                // 剩余部分为订单内容,合并成一个字符串(去掉第一部分)
                QString orderContent = parts.mid(2).join(" ");

                qDebug() << "桌号:" << tablenum;
                qDebug() << "订单内容:" << orderContent;

                // 判断桌号,并将对应订单信息放到对应框内
                if ("1号" == tablenum) {
                    ui->textEdit->setText(orderContent);
                }else if("2号" == tablenum){
                    ui->textEdit_6->setText(orderContent);
                }else if("3号" == tablenum){
                    ui->textEdit_7->setText(orderContent);
                }else if("4号" == tablenum){
                    ui->textEdit_8->setText(orderContent);
                }else if("5号" == tablenum){
                    ui->textEdit_9->setText(orderContent);
                }else if("6号" == tablenum){
                    ui->textEdit_10->setText(orderContent);
                }
            }else if(parts[0] == "CALL"){

                msgBox->setText("提示");
                msgBox->setInformativeText(QString(" %1 号桌顾客呼叫服务员").arg(parts[1]));
                msgBox->show();

            }else if(parts[0] == "PAY"){
                //QMessageBox::information(nullptr, "提示", QString(" %1桌顾客要结账").arg(parts[1]));
                msgBox->setText("提示");
                msgBox->setInformativeText(QString(" %1 桌顾客要结账").arg(parts[1]));
                msgBox->show();
            }
        });
    }
}

//清空
void MenuServer::on_pushButton_clicked()
{
    ui->textEdit->clear();
}

void MenuServer::on_pushButton_6_clicked()
{
    ui->textEdit_6->clear();
}

void MenuServer::on_pushButton_7_clicked()
{
    ui->textEdit_7->clear();
}

void MenuServer::on_pushButton_8_clicked()
{
    ui->textEdit_8->clear();
}

void MenuServer::on_pushButton_9_clicked()
{
    ui->textEdit_9->clear();
}

void MenuServer::on_pushButton_10_clicked()
{
    ui->textEdit_10->clear();
}
  • 客户端
    初始界面
    在这里插入图片描述

相关代码:widget.cpp 要进行二维码的识别,链接了百度人工智能的云端对二维码进行解析,二维码由草料二维码生成。
二维码背后蕴藏的实际上就是个字符串信息,此处二维码信息为“点菜”二字,若信息不对,是无法唤起后续界面的。

#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    setWindowTitle("点单");

    QPixmap pixmap(":/icon/1.jpg");
    QPixmap pix = pixmap.scaled(this->size());//解决图片与窗口大小不匹配的问题
    // 创建调色板
    QPalette palette;
    //palette.setBrush(QPalette::Background,QBrush(QPixmap(":/icon/jiemian.jpg")));
    palette.setBrush(QPalette::Background,QBrush(pix));
    // 应用调色板到窗口
    this->setPalette(palette);

    log = new login;

/*--------------------QRcode start-------------------*/
    filename = "D:/Qt/Project/Final/qrcode_baiduAI/pic/order.png";//文本信息:“点菜”
    ui->label_2->setPixmap(QPixmap("../orderClient/qrcode/order.png"));//二维码
    ui->label_2->setScaledContents(true);

    manager = new QNetworkAccessManager(this);
    //获取access_token值
    QString url = "https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=srWCVQuRjrRh68R88XMQR0nV&client_secret=0eO0z57vBvXA7FKyTPQpeGFzsY47vwk8";

    QNetworkRequest req;
    req.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");   //设置请求的头
    req.setUrl(QUrl(url));  //设置url
    manager->post(req, "");

    connect(manager, &QNetworkAccessManager::finished, this, &Widget::get_reply);
/*--------------------QRcode end-------------------*/

}

Widget::~Widget()
{
    delete ui;
}

/*--------------------QRcode-------------------*/
void Widget::get_reply(QNetworkReply *reply)  //接收响应并处理
{
    QByteArray data = reply->readAll();

    QJsonDocument doc = QJsonDocument::fromJson(data);
    if (doc.isEmpty())
        return;
    QJsonObject obj = doc.object();
    if (obj.contains("access_token")) {
        access_token = obj.value("access_token").toString();
    } else if (obj.contains("codes_result")) {
        QJsonArray arr = obj.value("codes_result").toArray();
        obj = arr.at(0).toObject();
        if (obj.contains("text")) {
   /*-----------------------获得了“点菜”的信息------------------------*/
            QString text = obj.value("text").toArray().at(0).toString();
            qDebug() << text;
   /*-----------------------唤起登录界面------------------------*/
            if("点菜" == text){
                // 获取当前主窗体的位置和大小
                QRect geom = this->geometry();
                // 计算新窗体的位置,使其居中于主窗体
                int x = geom.x() + (geom.width() - log->width()) / 2;
                int y = geom.y() + (geom.height() - log->height()) / 2;
                this->close();
                log->move(x, y);
                log->show();
            }
        }
    }
}

//识别二维码,得到“点菜”信息
void Widget::on_pushButton_clicked()
{
    QString url = "https://aip.baidubce.com/rest/2.0/ocr/v1/qrcode?access_token=" + access_token;

    QNetworkRequest req;
    req.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");   //设置请求的头
    req.setUrl(QUrl(url));  //设置url

    QFile file(filename);
    if (!file.open(QIODevice::ReadOnly)) {
        qDebug() << "error!";
        return;
    }
    QByteArray data = file.readAll();
    file.close();

    QByteArray body = "image=" + data.toBase64().toPercentEncoding();

    manager->post(req, body);
}

客户端点菜界面 为客户端的核心部分,用于构筑订单,提交需求,组件类别可以在ordermenu.ui文件里看到,这里不过多说明。
在这里插入图片描述
相关代码:ordermenu.cpp水平有限,实际上重复内容比较多,还望海涵

#include "ordermenu.h"
#include "ui_ordermenu.h"

OrderMenu::OrderMenu(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::OrderMenu)
{
    ui->setupUi(this);
    this->setWindowTitle("点单系统");
    //设置背景图片
    QPixmap pixmap(":/icon/3.jpg");
    QPixmap pix = pixmap.scaled(this->size());
    // 创建调色板
    QPalette palette;
    palette.setBrush(QPalette::Background,QBrush(pix));
    // 应用调色板到窗口
    this->setPalette(palette);

    /*---------------价格相关-----------------*/
    total = 0;  //总价
    msgBox = new QMessageBox(this);
    menuPrices = {  //价目表
        {"葱姜炒肉", 15},
        {"油炸排骨", 20},
        {"泡鲁达", 8},
        {"永昌火瓢牛肉", 80},
        {"滇西牛扒烀", 40},
        {"下村豆粉", 8},
        {"保山火烧肉", 32},
        {"傣族手抓饭", 150},
        {"云南凉虾", 5},
        {"云南甜米酒", 8}
    };

    ui->tabWidget->setCurrentIndex(0);

    /*---------------家常 pic-----------------*/
    ui->label_2->setPixmap(QPixmap(":/deshes/congjiang").scaled(ui->label_2->size(), \
                                                                Qt::KeepAspectRatio, Qt::SmoothTransformation));
    ui->label_10->setPixmap(QPixmap(":/deshes/youzhapaigu").scaled(ui->label_10->size(), \
                                                                   Qt::KeepAspectRatio, Qt::SmoothTransformation));
    /*---------------云南特色 pic-----------------*/
    ui->label_22->setPixmap(QPixmap(":/deshes/huopiaoniurou").scaled(ui->label_22->size(), \
                                                                     Qt::KeepAspectRatio, Qt::SmoothTransformation));
    ui->label_32->setPixmap(QPixmap(":/deshes/niupahu").scaled(ui->label_32->size(), \
                                                               Qt::KeepAspectRatio, Qt::SmoothTransformation));
    ui->label_35->setPixmap(QPixmap(":/deshes/xiacundoufen").scaled(ui->label_35->size(), \
                                                                    Qt::KeepAspectRatio, Qt::SmoothTransformation));
    ui->label_39->setPixmap(QPixmap(":/deshes/huoshaorou").scaled(ui->label_39->size(), \
                                                                  Qt::KeepAspectRatio, Qt::SmoothTransformation));
    ui->label_43->setPixmap(QPixmap(":/deshes/daizushuozhuafan").scaled(ui->label_43->size(), \
                                                                        Qt::KeepAspectRatio, Qt::SmoothTransformation));

    /*---------------饮品 pic-----------------*/
    ui->label_45->setPixmap(QPixmap(":/deshes/paoluda").scaled(ui->label_45->size(), \
                                                               Qt::KeepAspectRatio, Qt::SmoothTransformation));
    ui->label_51->setPixmap(QPixmap(":/deshes/liangxia").scaled(ui->label_51->size(), \
                                                                Qt::KeepAspectRatio, Qt::SmoothTransformation));
    ui->label_61->setPixmap(QPixmap(":/deshes/mijiu").scaled(ui->label_61->size(), \
                                                             Qt::KeepAspectRatio, Qt::SmoothTransformation));

    /*---------------家常 slots----------------------*/
    connect(ui->spinBox, SIGNAL(valueChanged(int)), this, \
            SLOT(on_spinBox_valueChanged(int)));    //葱姜炒肉
    connect(ui->spinBox_5, SIGNAL(valueChanged(int)), this, \
            SLOT(on_spinBox_valueChanged(int)));    //油炸排骨
    /*---------------饮品 slots----------------------*/
    connect(ui->spinBox_32, SIGNAL(valueChanged(int)), this, \
            SLOT(on_spinBox_valueChanged(int)));    //泡鲁达
    connect(ui->spinBox_40, SIGNAL(valueChanged(int)), this, \
            SLOT(on_spinBox_valueChanged(int)));    //云南凉虾
    connect(ui->spinBox_41, SIGNAL(valueChanged(int)), this, \
            SLOT(on_spinBox_valueChanged(int)));    //云南甜米酒

    /*---------------云南特色 slots----------------------*/
    connect(ui->spinBox_20, SIGNAL(valueChanged(int)), this, \
            SLOT(on_spinBox_valueChanged(int)));    //火瓢牛肉
    connect(ui->spinBox_23, SIGNAL(valueChanged(int)), this, \
            SLOT(on_spinBox_valueChanged(int)));    //扒烀
    connect(ui->spinBox_24, SIGNAL(valueChanged(int)), this, \
            SLOT(on_spinBox_valueChanged(int)));    //下村
    connect(ui->spinBox_26, SIGNAL(valueChanged(int)), this, \
            SLOT(on_spinBox_valueChanged(int)));    //火烧
    connect(ui->spinBox_29, SIGNAL(valueChanged(int)), this, \
            SLOT(on_spinBox_valueChanged(int)));    //手抓

    /*---------------------关于网络--------------------------*/
    socket = new QTcpSocket;
    host = "192.168.20.1";
    port = 9999;
    socket->connectToHost(host, port);//连接

}

OrderMenu::~OrderMenu()
{
    delete ui;
}

//构建订单
void OrderMenu::on_spinBox_valueChanged(int arg1)
{
    // 获取发送信号的对象(即哪个 QSpinBox 发生了变化)
    QSpinBox *spinbox = qobject_cast<QSpinBox *>(sender());
    if (!spinbox) return; // 确保转换成功

    QString itemName;
    total = 0;

    // 根据不同的 QSpinBox 设置对应的 item 名称
    // 可以继续添加更多的 QSpinBox 和对应项...
    if (spinbox == ui->spinBox) {
        itemName = "葱姜炒肉";
    }else if(spinbox == ui->spinBox_5){
        itemName = "油炸排骨";
    }else if(spinbox == ui->spinBox_32){
        itemName = "泡鲁达";
    }else if(spinbox == ui->spinBox_20){
        itemName = "永昌火瓢牛肉";
    }else if(spinbox == ui->spinBox_23){
        itemName = "滇西牛扒烀";
    }else if(spinbox == ui->spinBox_24){
        itemName = "下村豆粉";
    }else if(spinbox == ui->spinBox_26){
        itemName = "保山火烧肉";
    }else if(spinbox == ui->spinBox_29){
        itemName = "傣族手抓饭";
    }else if(spinbox == ui->spinBox_40){
        itemName = "云南凉虾";
    }else if(spinbox == ui->spinBox_41){
        itemName = "云南甜米酒";
    }

    //看listWidget内是否已经有该菜
    auto tmp = ui->listWidget->findItems(itemName, Qt::MatchContains);
    //若有
    if (arg1 == 0) {
        // 当值为 0 时,删除对应项
        if (!tmp.isEmpty()) {
            QListWidgetItem *item = tmp.first();
            delete item; // 删除找到的第一项
        }
    } else {
        // 当值不为 0 时,增加或更新对应项
        if (!tmp.isEmpty()) {
            QListWidgetItem *item = tmp.first();
            item->setText(QString("%1 %2 份").arg(itemName).arg(arg1)); // 更新数量
        } else {
            ui->listWidget->addItem(QString("%1 %2 份").arg(itemName).arg(arg1)); // 添加新项
        }
    }

    // 更新 total 显示到 lineEdit 上
    total = (ui->spinBox->value() * menuPrices["葱姜炒肉"]) +
            (ui->spinBox_5->value() * menuPrices["油炸排骨"]) +
            (ui->spinBox_32->value() * menuPrices["泡鲁达"]) +
            (ui->spinBox_20->value() * menuPrices["永昌火瓢牛肉"]) +
            (ui->spinBox_23->value() * menuPrices["滇西牛扒烀"]) +
            (ui->spinBox_24->value() * menuPrices["下村豆粉"]) +
            (ui->spinBox_26->value() * menuPrices["保山火烧肉"]) +
            (ui->spinBox_29->value() * menuPrices["傣族手抓饭"]) +
            (ui->spinBox_40->value() * menuPrices["云南凉虾"]) +
            (ui->spinBox_41->value() * menuPrices["云南甜米酒"]);
    ui->lineEdit->setText(QString::number(total));
}

//提交订单给服务器
void OrderMenu::on_pushButton_clicked()
{
    /*--------------构筑订单内容-------------*/
    result.clear();
    //添加发送内容类型
    result += "ORDER ";

    //添加桌号
    result += ui->spinBox_2->text() + " ";

    //添加菜和价格
    for(int i=0; i<ui->listWidget->count(); i++){   //遍历订单内容
        QListWidgetItem *tmp = ui->listWidget->item(i);
        if(tmp){
            result += tmp->text() + "\n";
            //            result += tmp->text();
        }
    }
    //添加价格信息,与结束标志
    result += QString("总计 %1 元 END").arg(ui->lineEdit->text());
    qDebug() << result << endl;


    //发送订单
    socket->write(result.toUtf8());
    QThread::usleep(100);
}

//结账
void OrderMenu::on_pushButton_2_clicked()
{
    //QMessageBox::information(nullptr, "尊敬的顾客", "服务员正在前往,请稍后...");
    msgBox->setText("尊敬的顾客");
    msgBox->setInformativeText("服务员正在前往,请稍后...");
    msgBox->show();
    result.clear();
    //添加发送内容类型
    result += "PAY ";
    //添加桌号和结束标志
    result += ui->spinBox_2->text() + " END";
    socket->write(result.toUtf8());
    QThread::usleep(100);
}

// 呼叫服务员
void OrderMenu::on_pushButton_3_clicked()
{
    //QMessageBox::information(nullptr, "尊敬的顾客", "服务员正在前往,请稍后...");

    msgBox->setText("尊敬的顾客");
    msgBox->setInformativeText("服务员正在前往,请稍后...");
    msgBox->show();
    result.clear();
    //添加发送内容类型
    result += "CALL ";
    //添加桌号和结束标志
    result += ui->spinBox_2->text() + " END";
    socket->write(result.toUtf8());
    QThread::usleep(100);
}

3. 问题与解决

3.1 界面背景图片设置: 如图所示,这是其中一个界面的样式,可以看到该界面由一个 QWidget、一个QLabel和一个 QPushButton组成;
在这里插入图片描述
单纯使用“编辑样式表”,并不能完成以上“仅设置背景的需求”,label 和 button 同样会被设置上图片
在这里插入图片描述

  • 解决: 使用 QPalettesetBrush方法,即可完成需求
   QPixmap pixmap(":/icon/jiemian.jpg");
   QPixmap pix = pixmap.scaled(this->size()); //设置背景图片适应窗口尺寸
   // 创建调色板
   QPalette palette;
   palette.setBrush(QPalette::Background,QBrush(pix));
   // 应用调色板到窗口
   this->setPalette(palette);

3.2 数据交互逻辑有误: 实际上是对 QT信号与槽机制的认知有误

  • 错误设计:
    简单的TCP通信流程:客户端先发消息类型,再发消息体;客户端先接收消息类型,再接收消息体,并做不同的处理,在 C语言网络编程时这种设计是没有问题的;
    但在 QTSocket 通信是通过信号与槽机制来完成数据交互的:也就是说,“客户端先发消息类型,再发消息体”,触发的都是同一个信号,对于服务器而言两次都是接收消息类型,并执行对消息类型的处理。
	connect(socket, &QTcpSocket::readyRead, [&](){
	static QByteArray buffer;
	buffer.append(socket->readAll());
  • 改进设计:模仿 HTTP 网络传输的协议,客户端将 消息类型消息体用字符串拼接操作为简单数据体,最后再加上一个结束标志;服务器接受完整消息,并逐步解析解析消息。
    • 客户端发送订单消息代码 QString result; 用于存储消息体
//提交订单给服务器
void OrderMenu::on_pushButton_clicked()
{
    /*--------------构筑订单内容-------------*/
    result.clear();
    //添加发送内容类型
    result += "ORDER ";//消息头

    //添加桌号
    result += ui->spinBox_2->text() + " ";

    //添加菜和价格
    for(int i=0; i<ui->listWidget->count(); i++){   //遍历订单内容
        QListWidgetItem *tmp = ui->listWidget->item(i);
        if(tmp){
            result += tmp->text() + "\n";
            //            result += tmp->text();
        }
    }
    //添加价格信息,与结束标志
    result += QString("总计 %1 元 END").arg(ui->lineEdit->text());
    qDebug() << result << endl;

    //发送订单
    socket->write(result.toUtf8());
    QThread::usleep(100);
}

服务器解析消息体

//接收客户端连接,识别消息类型,根据消息类型做不同的事情
void MenuServer::accept_client(){
    if (server->hasPendingConnections()) { //判断是否有等待的连接
        socket = server->nextPendingConnection();  //接收连接

        /*--------识别消息类型,根据消息类型做不同的事情--------*/
        connect(socket, &QTcpSocket::readyRead, [&](){
            static QByteArray buffer;
            buffer.append(socket->readAll());

            // 检查是否包含 'END' 分隔符以处理完整消息
            int endIndex = buffer.indexOf("END");
            QByteArray messageBytes = buffer.left(endIndex); // 获取完整消息(不包括 'END')

            // 移除已处理的部分(包括 'END' 的长度)
            buffer.remove(0, endIndex + 3);

            QString message = QString::fromUtf8(messageBytes).trimmed(); // 转换为 QString,并去除多余空格

            // 分析消息内容
            QStringList parts = message.split(" ", QString::SkipEmptyParts);

            if (parts.size() > 2 && parts[0] == "ORDER") { // 确保有足够的部分并且是 ORDER 消息
                QString orderType = parts[0]; // "ORDER"
                QString tablenum = parts[1];   // 桌号

                // 剩余部分为订单内容,合并成一个字符串(去掉第一部分)
                QString orderContent = parts.mid(2).join(" ");

                qDebug() << "桌号:" << tablenum;
                qDebug() << "订单内容:" << orderContent;

                // 判断桌号,并将对应订单信息放到对应框内
                if ("1号" == tablenum) {
                    ui->textEdit->setText(orderContent);
                }else if("2号" == tablenum){
                    ui->textEdit_6->setText(orderContent);
                }else if("3号" == tablenum){
                    ui->textEdit_7->setText(orderContent);
                }else if("4号" == tablenum){
                    ui->textEdit_8->setText(orderContent);
                }else if("5号" == tablenum){
                    ui->textEdit_9->setText(orderContent);
                }else if("6号" == tablenum){
                    ui->textEdit_10->setText(orderContent);
                }
            }else if(parts[0] == "CALL"){
                msgBox->setText("提示");
                msgBox->setInformativeText(QString(" %1 号桌顾客呼叫服务员").arg(parts[1]));
                msgBox->show();

            }else if(parts[0] == "PAY"){
                //QMessageBox::information(nullptr, "提示", QString(" %1桌顾客要结账").arg(parts[1]));
                msgBox->setText("提示");
                msgBox->setInformativeText(QString(" %1 桌顾客要结账").arg(parts[1]));
                msgBox->show();
            }
        });
    }
}

特别鸣谢

在这里插入图片描述
文案:張嘉鑫
逻辑视图:張嘉鑫
代码:張嘉鑫
其余图片:来自网络
Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐