基于Qt的收银点餐系统之多个相同子控件与父控件间的交互

待解决问题:

在这个收银点餐系统中,我将每个商品的信息放在button(子控件)中,希望当点击某个按钮时,主面板(父控件)有如下变化:
1.总价格进行相应的变化
2.右下方出现商品名和数量
如图所示:
这里写图片描述
要实现上面的两个功能,就需要解决下面的几个问题:
1.如何在QPushButton里面存放商品的信息数据;
2.如何将数据从button传给主面板
3.那么多个商品,难道要在父控件的控制代码中添加那么多on_clicked_button()? 但是on_clicked_button()里执行的逻辑仅仅只是参数不同,其他都一样,如何避免怎样的无用操作?


一、解决方案的概述

由于总价格进行变化以及右下方出现相关记录是同一性质的问题,即多个子控件与父控件之间的交互问题。这种交互的特点有:
1.子控件的类型相同(本文中均为特殊的按钮);
2.由子控件触发后,在父控件中执行相同的逻辑功能。
之所以指出这样的特点,是因为它与(以后的一片文章)中提到的交互有所不同。

下面仅对前者进行详细说明。

一开始要实现这两个功能的时候,我的状况是这样的:第一问知道可以自己写一个类;第二问应该是和信号槽有关吧,但是并不知道怎么用;第三问完全没有思路。到后来发现,第三问也可以用信号槽机制,而且针对第三问的解决其实就一条connect语句。真正的难点,是如何设计相关的信号和槽、信号和槽要放在哪里(子控件还是父控件)。

解决方案:
1.定制一个goodsButton类,该类继承了QPushButton。goodsButton类存储了商品信息,可以在它被点击后发送一个携带商品价格的signal。
2.在父控件中添加相关的slot函数,用于接受signal传来的信息以及进行相关的逻辑处理(对总价格进行变化)
3.在父控件的代码中生成若干goodsButton的对象并添加这些对象,对每一个对象使用connect语句将goodsButton的signal和父控件的slot连接起来

二、goodsButton类的实现

goodsButton.h

#ifndef GOODSBUTTON_H
#define GOODSBUTTON_H
#include <QPushButton>
#include <QMouseEvent>
#include <QString>

class goodsButton : public QPushButton
{   Q_OBJECT
    private:
        int ID;   //简单的只存了一个id,并用button的text()存价格信息,若要拓展信息,则相应的添加变量就可以了
    public:
        goodsButton(int);
        void mousePressEvent(QMouseEvent*);  //鼠标点击事件触发goodsButtonClicked信号的发送
        ~goodsButton();
    signals:
        void goodsButtonClicked(int,QString);  //携带商品价格的signal
};

#endif // GOODSBUTTON_H

goodsButton.cpp

#include "goodsbutton.h"

goodsButton::goodsButton(int id)
{
    ID = id;
    this->setText(QString::number(ID));
}

void goodsButton::mousePressEvent(QMouseEvent *e){
    if(e->button()==Qt::LeftButton){
        emit goodsButtonClicked(this->ID,this->text());  //发送携带商品价格的signal  (emit是保留字)
    }
}

goodsButton::~goodsButton()
{

}

三、goodsList的实现

goodsList.h

#ifndef GOODSLIST_H
#define GOODSLIST_H

#include <QMainWindow>
#include <QGroupBox>
#include <QScrollArea>
#include <QListWidget>
#include <QTextBrowser>
#include <QLabel>
#include <QPushButton>
#include <QSpinBox>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QGridLayout>
#include <QWidget>
#include <QStackedLayout>

#include <goodsbutton.h>

class goodsList : public QMainWindow
{
    Q_OBJECT

public:
    explicit goodsList(QWidget *parent = 0);
    ~goodsList();

private:
    void createCategoryAndGoodslist();  //左侧category的布局
    void createInfo();  //右侧Info的布局
    void createWholeLayout(); //主窗口布局

    //在中间goodsList区域创建可滑动区域
    QScrollArea* create_area();

    enum {NUMGOODS = 50,NUMBUTTONS = 4};

    QGroupBox *categoryGroupBox;
    QGroupBox *infoGroupBox;
    QStackedLayout *goodsDisplayLayout; //存放三个QScrollArea,并实现与存放在categoryListWidget的分类标签关联
    QListWidget *categoryListWidget;
    QLabel *priceLabel;
    QPushButton *operationButton[NUMBUTTONS];
    QSpinBox *countSpinBox[NUMGOODS];
    QWidget *buttonsWidget;
    QWidget *countWidget;

    goodsButton *goodsGoodsButton[NUMGOODS];  //goodsButton

private slots:
    void updatePrice(int,QString); //接收goodsButton传来的价格信息并进行逻辑处理的Slot:更新价格
};

#endif // GOODSLIST_H

goodsList.cpp

#include "goodslist.h"
#include "QString"
#include <qglobal.h>
#include <QStringList>

goodsList::goodsList(QWidget *parent) :
    QMainWindow(parent)
{
     createCategoryAndGoodslist();
     createInfo();
     createWholeLayout();
}


void goodsList::createCategoryAndGoodslist()
{
    //准备左侧category
    categoryListWidget = new QListWidget;
    categoryListWidget->addItem(tr("分类一"));
    categoryListWidget->addItem(tr("分类二"));
    categoryListWidget->addItem(tr("分类三"));

    QVBoxLayout *categoryLayout = new QVBoxLayout;
    categoryLayout->addWidget(categoryListWidget);

    categoryGroupBox = new QGroupBox(tr("Category"));
    categoryGroupBox->setLayout(categoryLayout);

    //准备中间goodsList并把三个widget添加入stackedLayout中
    goodsDisplayLayout = new QStackedLayout();
    for(int i = 0; i < 3;i++){
        QScrollArea *area = create_area();
        goodsDisplayLayout->addWidget(area);
    }

    //将category和goodsList关联起来
    connect(categoryListWidget,SIGNAL(currentRowChanged(int)),goodsDisplayLayout,SLOT(setCurrentIndex(int)));
    categoryListWidget->setCurrentRow(0);

}

void goodsList::createInfo()
{
    priceLabel = new QLabel(tr("total price: 0"));

    //组装4个button
    buttonsWidget = new QWidget;
    QGridLayout *buttonsLayout = new QGridLayout;
    for(int i = 0; i < NUMBUTTONS; i++)
    {
        operationButton[i] = new QPushButton(tr("Button %1").arg(i + 1));
        buttonsLayout->addWidget(operationButton[i],i/2,i%2);
    }
    buttonsWidget->setLayout(buttonsLayout);

    //组装购买商品信息
    countWidget = new QWidget;
    QGridLayout *countLayout = new QGridLayout;
    for(int i = 0; i < 5; i++)
    {
        countSpinBox[i] = new QSpinBox;
        countLayout->addWidget(countSpinBox[i],i / 2,i%2);
    }
    countWidget->setLayout(countLayout);

    //Info部分的总布局
    QVBoxLayout *infoLayout = new QVBoxLayout;
    infoLayout->addWidget(priceLabel);
    infoLayout->addWidget(buttonsWidget);
    infoLayout->addWidget(countWidget);

    infoGroupBox = new QGroupBox(tr("Info"));
    infoGroupBox->setLayout(infoLayout);
}


//主窗口的布局
void goodsList::createWholeLayout()
{
    QHBoxLayout *wholeLayout = new QHBoxLayout;
    wholeLayout->addWidget(categoryGroupBox);
    wholeLayout->addLayout(goodsDisplayLayout);
    wholeLayout->addWidget(infoGroupBox);

    //设置拉伸因子
    wholeLayout->setStretchFactor(categoryGroupBox,1);
    wholeLayout->setStretchFactor(goodsDisplayLayout,3);
    wholeLayout->setStretchFactor(infoGroupBox,2);

    //这部分要有,否则主窗口不会有显示
    QWidget *widget = new QWidget(this) ;
    widget->setLayout(wholeLayout);
    this->setCentralWidget(widget);
    setWindowTitle(tr("收银点餐系统"));

}


//以上和上一篇文章的代码大致相同,从下面开始不同
QScrollArea* goodsList::create_area(){
//    int NUM = 50;
    //在父控件的代码中生成若干goodsButton的对象并添加这些对象
    QGridLayout *layout = new QGridLayout();
    for(int i = 0; i < NUMGOODS; i++){
        goodsGoodsButton[i] = new goodsButton(i);
        layout->addWidget(goodsGoodsButton[i],i/4,i%4);
        goodsGoodsButton[i]->setText(QString("guoba%1 %2 yuan/dai").arg(i).arg((qrand()%10)+1.5));

        //划重点! 对每一个对象使用connect语句将goodsButton的signal和父控件的slot连接起来
        QObject::connect(goodsGoodsButton[i],SIGNAL(goodsButtonClicked(int,QString)),this,SLOT(updatePrice(int,QString)));
    }


    QWidget *wg = new QWidget();
    wg->setLayout(layout);
    QScrollArea *area = new QScrollArea();
    area->setWidget(wg);
    return area;
}

void goodsList::updatePrice(int id, QString text){
    QStringList list = text.split(" ");
    double newPrice = list.at(1).toDouble();

    QString priceLabelText = priceLabel->text();
    QStringList list2 = priceLabelText.split(" ");

    double lastPrice = list2.at(2).toDouble();
    double curPrice = newPrice + lastPrice;

    priceLabel->setText(QString("total price: %1").arg(curPrice));
}


goodsList::~goodsList()
{
}

参考资料:
Qt中如何才能让子窗口按钮响应到父类窗口上的槽函数:https://zhidao.baidu.com/question/320078001.html
拓展:
Qt窗体之间相互传值的三种方式:http://blog.csdn.net/zbw1185/article/details/48519371
(另外的说法: 1、使用信号槽机制进行传递 2、自己实现观察者模式 3、自定义Event)
QSignalMapping:https://doc.qt.io/archives/qq/qq10-signalmapper.html
(具体用法见本人的另一篇文章)

采用C/S模式,完成一前台(服务器)对多客服端通讯,用Mysql数据库保存信息; 主要技术: 1.采用TCP/IP协议,容器完成服务端与多客户端的链接 服务端: server=new QTcpServer(this);建立端口 server->listen(QHostAddress::Any,PORT);监听端口 connect(server,SIGNAL(newConnection()),this,SLOT(accpetConnection()));等待用户链接 QTcpSocket* temp = server->nextPendingConnection();建立链接 client.push_back(temp);用户压栈 connect(temp,SIGNAL(readyRead()),this,SLOT(readData()));当端口有数据就读 读数据时先用迭代器遍历容器找到发送信息的客户端,再解析数据并响应 客户端: client=new QTcpSocket(this);建立端口 client->connectToHost(IP,PORT);链接主机 connect(client,SIGNAL(readyRead()),this,SLOT(readData()));端口有数据就读 2.界面布局 服务器 a.主菜单,预订,开台,换台,电账单功能项采用QToolButton文字置于图片下面,水平布局 b.当前餐台信息与总餐台状态信息用QLabel垂直布局放于主窗体左侧 c.餐台信息采用QGraphicsView+QGraphicsScene+QGraphicsItem布局,view与item需要重写自己的类,Item包括图片与文本信息;将Item放入墙纸scene中,墙纸scene贴到墙view上完成显示。 客户端 a.选择桌号与人数用QLabel,对应的下拉选项用QComboBox,确认,呼叫与结账功能用QToolButton,这些控件水平布局放置于窗体最上方 b.左侧用QTabWidget其中加入特价菜单与我的菜单两个窗体 c.中为QGraphicsView+QGraphicsScene+QGraphicsItem布局,布局菜单图片与价格名称,菜单信息服务器发送至客户端与客户端的图片匹配起来。 d.右侧为菜单类型分类按键,采用垂直布局 3.信号与槽机制的运用 a.预订,开台,换台功能的实现:点击对应的按钮触发clicked()信号,与之对应的槽函数中QToolButton *btn = (QToolButton *)sender();区分信号源,弹出对应的窗体让用户输入相应信息,按确定按钮修改SQL对应Table内容然后发送输入信息信号,主窗体接受到信号调用槽函数(相应窗体成员调用其布局函数重布局)重新布局整个界面(餐台信息与左侧总餐台状态同时更新) b.鼠标悬浮于餐台信息Item时图片放大:改写了QGraphicsSceneMouseEvent事件实现 c.主菜单与电账单的显示采用QSqlTableModel+QTableView加载整个SQL中相应的Table显示Table内容
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值