待解决问题:
在这个收银点餐系统中,我将每个商品的信息放在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
(具体用法见本人的另一篇文章)