多个信号连接一个槽
多个信号连接一个槽常用于多个对象处理同一类型的事情,在消费类软件中十分常见,例如黄骑士送餐软件,其UI是由很多按钮或自定义的小部件组成的 ,在这里实现一个简单的点餐界面, 目前只显示点餐名称,还没有加上其他信息。
以下是创建控件以及信号连接的部分:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QGridLayout>
#include <QPushButton>
#include <QMessageBox>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
//创建栅格布局
QGridLayout *layout = new QGridLayout(ui->centralWidget);
//为主页面设置栅格布局
ui->centralWidget->setLayout(layout);
QStringList list_menu = {"热干面", "牛肉拌面", "三鲜豆皮", "油饼包烧麦", "包子", "面窝", "糊汤粉", "鸡冠饺", "油酥饼", "欢喜坨", "糯米鸡", "糊汤米酒"};
//创建菜单按钮,加入栅格布局中,
int menu_count = list_menu.count();
for(int i = 0; i < menu_count; i++){
//创建菜单
QPushButton *btn = new QPushButton(this);
//设置可以选中的状态
btn->setCheckable(true);
btn->setChecked(false);
//设置菜单名称
btn->setText(list_menu[i]);
//设置按钮大小
btn->setFixedSize(150,55);
//加入布局
layout->addWidget(btn, i/4, i%4);
//将按钮的点击信号连接到同一个槽
connect(btn, SIGNAL(clicked()), this, SLOT(onButtonClicked()));
}
//最后一行布局中加入label控件
label = new QLabel(this);
//布局,插入第3行,第0列,不跨行,跨4列, 左对齐
layout->addWidget(label, menu_count/4, 0, 1, 4, Qt::AlignLeft);
//设置最小宽度
label->setMinimumWidth(150 * 4);
//文字过多,自动换行
label->setWordWrap(true);
//设置按钮样式表
setStyleSheet("QPushButton{color: white; background:#3465a4; border:none;}"
"QPushButton::hover{ background:#729fcf;}"
"QPushButton::pressed{ background:#204a87;}"
"QPushButton::checked{ background:#204a87;}"
);
}
以下是被所有信号连接的槽函数:
void MainWindow::onButtonClicked()
{
QPushButton* btn = (QPushButton*)sender();
if (!btn)
return;
//获取当前显示的文本
QStringList list_text = label->text().split(" ");
if (btn->isChecked()) {
btn->setIcon(QIcon("://check.png"));
//追加所选中的早点
list_text.append( btn->text() );
} else {
btn->setIcon(QIcon(""));
//移除所选中的早点
list_text.removeOne(btn->text());
}
//显示当前选中的早点
label->setText( list_text.join(" ") );
}
1. 在代码中,所有按钮的点击事件都连接上了onButtonClicked槽函数,其目的是一致的,为了获取点餐名称。
2. 在槽函数中使用了sender是为了得知哪个按钮被点击了,直接调用槽函数是得不到按钮对象的,只有通过点击触发进入槽函数才可以获取到按钮对象。
3. 在实际场景下,仅仅以text属性作为用户数据,过于简陋了。如果需要增加额外的数据,比如编号、单价、份数、折扣等,可以在类中使用数据结构,将控件与其它数据做关联;也可以使用QPushButton的数据接口setUserData和userData方法来处理用户数据。
用户数据接口
这里推荐使用用户数据接口: setUserData和userData, 其它widgets控件的也基本是一个套路。在对应的控件中存储数据是非常有必要的,可以减少全局的数据结构,也让数据与控件的交互更加便捷。
上述的例子改造后,我们来看看效果:
对上面的例子做出了如下修改:
1. 将界面用设计器实现了一部分,按钮的创建还是在代码中进行。
2. 定义了用户数据结构体,将名称和价格放入其中。
//用户数据
struct breakfastData : QObjectUserData
{
QString name;
double price;
};
以下是本例子完整的代码:
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QLabel>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void onButtonClicked();
private:
Ui::MainWindow *ui;
QStringList m_list_menu;
double m_total_price;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QGridLayout>
#include <QPushButton>
#include <QMessageBox>
//用户数据
struct breakfastData : QObjectUserData
{
QString name;
double price;
};
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
//初始化菜单配置
const QStringList list_menu = {"热干面", "牛肉拌面", "三鲜豆皮", "油饼包烧麦", "包子", "面窝", "糊汤粉", "鸡冠饺", "油酥饼", "欢喜坨", "糯米鸡", "糊汤米酒"};
const QList<double> list_price = {6.0, 12.0, 8.5, 7.0, 10.0, 2.5, 5.0, 2.5, 2.0, 1.5, 3.0, 3.0};
int menu_count = list_menu.count();
//创建菜单按钮,加入栅格布局中,
for(int i = 0; i < menu_count; i++){
//创建菜单
QPushButton *btn = new QPushButton(this);
//设置可以选中的状态
btn->setCheckable(true);
btn->setChecked(false);
//设置用户数据
breakfastData* data = new breakfastData;
data->name = list_menu[i];
data->price = list_price[i];
btn->setUserData(Qt::UserRole, data);
//设置菜单名称
btn->setText(list_menu[i]);
//设置按钮大小
btn->setFixedSize(150,55);
//加入布局
ui->gridLayout->addWidget(btn, i/4, i%4);
//将按钮的点击信号连接到同一个槽
connect(btn, SIGNAL(clicked()), this, SLOT(onButtonClicked()));
}
//初始化数据
m_list_menu.clear();
m_total_price = 0.0;
//设置按钮样式表
setStyleSheet("QPushButton{color: white; background:#3465a4; border:none;}"
"QPushButton::hover{ background:#729fcf;}"
"QPushButton::pressed{ background:#204a87;}"
"QPushButton::checked{ background:#204a87;}"
);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::onButtonClicked()
{
QPushButton* btn = (QPushButton*)sender();
if (!btn)
return;
//获取用户数据
breakfastData* data = (breakfastData*)btn->userData(Qt::UserRole);
if (btn->isChecked()) {
btn->setIcon(QIcon("://check.png"));
//添加此早点
if (!m_list_menu.contains(data->name)){
m_list_menu.append(data->name);
m_total_price += data->price;
}
} else {
btn->setIcon(QIcon(""));
//移除所选中的早点
if (m_list_menu.contains(data->name)){
m_list_menu.removeOne(data->name);
m_total_price -= data->price;
}
}
//结算
if (!m_list_menu.isEmpty())
ui->label_menu->setText( m_list_menu.join(" ") );
else
ui->label_menu->setText("");
//显示总金额
ui->label_result->setText(QString::number(m_total_price));
}