基于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
(具体用法见本人的另一篇文章)

猜你喜欢

转载自blog.csdn.net/qq_35153254/article/details/78298662