目录
1、设计与实现
2、Qt信号与槽基础
界面设计
-定义组件间的间隔 Space = 10px
-定义按钮组件的大小 Width = 40px, Height = 40px
-定义文本框组件的大小 Width = 5 * 40px + 4 * 10px, Height = 30px
-计算器程序不需要最大化和最小化按钮 ,窗口固定大小 ,文本框不能直接输入字符
计算机器界面实现
#include
#include
#include
#include
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget* w = new QWidget(NULL, Qt::WindowCloseButtonHint);
QLineEdit* le = new QLineEdit(w);
QPushButton* button[20] = {0};
const char* btnText[20] =
{
"7", "8", "9", "+", "(",
"4", "5", "6", "-", ")",
"1", "2", "3", "*", "<-",
"0", ".", "=", "/", "C",
};
int ret = 0;
le->move(10, 10);
le->resize(240, 30);
le->setReadOnly(true);
for(int i=0; i<4; i++)
{
for(int j=0; j<5; j++)
{
button[i*5 + j] = new QPushButton(w);
button[i*5 + j]->resize(40, 40);
button[i*5 + j]->move(10 + (10 + 40)*j, 50 + (10 + 40)*i);
button[i*5 + j]->setText(btnText[i*5 + j]);
}
}
w->show();
w->setFixedSize(w->width(), w->height());
ret = a.exec();
delete w;
return ret;
}
计算器界面代码重构
计算器界面代码重构 使用到二阶构造模式
QCalculatorUI.h
#ifndef _QCALCULATORUI_H_
#define _QCALCULATORUI_H_
#include
#include
#include
class QCalculatorUI : public QWidget
{
private:
QLineEdit* m_edit;
QPushButton* m_buttons[20];
QCalculatorUI();
bool construct();
public:
static QCalculatorUI* NewInstance();
void show();
~QCalculatorUI();
};
#endif
QCalculatorUI.cpp
#include "QCalculatorUI.h"
QCalculatorUI::QCalculatorUI() : QWidget(NULL, Qt::WindowCloseButtonHint)
{
}
bool QCalculatorUI::construct()
{
bool ret = true;
const char* btnText[20] =
{
"7", "8", "9", "+", "(",
"4", "5", "6", "-", ")",
"1", "2", "3", "*", "<-",
"0", ".", "=", "/", "C",
};
m_edit = new QLineEdit(this);
if( m_edit != NULL )
{
m_edit->move(10, 10);
m_edit->resize(240, 30);
m_edit->setReadOnly(true);
}
else
{
ret = false;
}
for(int i=0; (i<4) && ret; i++)
{
for(int j=0; (j<5) && ret; j++)
{
m_buttons[i*5 + j] = new QPushButton(this);
if( m_buttons[i*5 + j] != NULL )
{
m_buttons[i*5 + j]->resize(40, 40);
m_buttons[i*5 + j]->move(10 + (10 + 40)*j, 50 + (10 + 40)*i);
m_buttons[i*5 + j]->setText(btnText[i*5 + j]);
}
else
{
ret = false;
}
}
}
return ret;
}
QCalculatorUI* QCalculatorUI::NewInstance()
{
QCalculatorUI* ret = new QCalculatorUI();
if( (ret == NULL) || !ret->construct() )
{
delete ret;
ret = NULL;
}
return ret;
}
void QCalculatorUI::show()
{
QWidget::show();
setFixedSize(width(), height());
}
QCalculatorUI::~QCalculatorUI()
{
}
main.cpp
#include
#include "QCalculatorUI.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QCalculatorUI* cal = QCalculatorUI::NewInstance();
int ret = -1;
if( cal != NULL )
{
cal->show();
ret = a.exec();
delete cal;
}
return ret;
}
Qt中的消息处理机制
-信号(Signal):由操作系统产生的消息
-槽(Slot) :程序中的消息处理函数
-连接(Connect) :将系统消息绑定到消息处理函数
-信号到槽的连接必须发生在两个Qt类对象之间!
// sender: 发出信号的QObject子类
// receiver: 接收信号的QObject子类
// signal: 消息名
// method: 接收对象的消息处理函数名
bool QObject::connect(const QObject* sender, const char* signal, const QObject* receiver, const char* method, Qt::ConnectionType type = Qt::AutoConnection);
// 在Qt中,消息用字符串进行描述,connect函数在消息名和处理函数之间建立映射
// SIGNAL (宏):用于指定消息名
// SLOT :用于指定消息处理函数名
// 示例
#include
#include
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QPushButton b;//成为顶层窗口
b.setText("Click me to quit!");
b.show();
QObject::connect(&b,SIGNAL(clicked()),&a,SLOT(quit()));
//将按钮对象的点击消息映射到a对象的quit函数
return a.exec();
}
自定义槽
-只有QObject的子类才能自定义槽 ,定义槽的类必须在声明的最开始处使用Q_OBJECT
-类中声明槽时需要使用slots关键字
-槽与所处理的信号在函数签名上必须一致
-SIGNAL和SLOT所指定的名称中: 可以包含参数类型,不能包含具体的参数名
为计算器实例添加消息处理函数
QCalculatorUI.h
#ifndef _QCALCULATORUI_H_
#define _QCALCULATORUI_H_
#include
#include
#include
class QCalculatorUI : public QWidget
{
Q_OBJECT // 定义槽的类必须在声明的最开始处使用Q_OBJECT
private:
QLineEdit* m_edit;
QPushButton* m_buttons[20];
QCalculatorUI();
bool construct();
private slots:
void onButtonClicked(); // 消息处理函数
public:
static QCalculatorUI* NewInstance();
void show();
~QCalculatorUI();
};
#endif
QCalculatorUI.cpp
#include
CalculatorUI::CalculatorUI()
{
}
bool CalculatorUI::construct()
{
bool ret = true;
const char* btnText[] =
{
"7", "8", "9", "+", "(",
"4", "5", "6", "-", ")",
"1", "2", "3", "*", "<-",
"0", ".", "=", "/", "C",
};
m_edit = new QLineEdit(this);
if( m_edit )
{
m_edit->move(10,10);
m_edit->resize(240,30);
m_edit->setReadOnly(true);
m_edit->setAlignment(Qt::AlignRight);
}
else
{
ret = false;
}
for(int i = 0; i < 4 && ret; i++)
{
for(int j = 0; j < 5 && ret; j++)
{
m_buttons[i * 5 + j] = new QPushButton(this);
if( m_buttons[i * 5 + j] )
{
m_buttons[i * 5 + j]->resize(40, 40);
m_buttons[i * 5 + j]->move(10 + (10 + 40)*j, 50 + (10 + 40)*i);
m_buttons[i * 5 + j]->setText(btnText[i * 5 + j]);
QObject::connect(m_buttons[i * 5 + j], SIGNAL(clicked()),this, SLOT(onButtonClicked())); // 连接信号与槽
}
else
{
ret = false;
}
}
}
return ret;
}
CalculatorUI* CalculatorUI::NewInstance()
{
CalculatorUI* ret = new CalculatorUI();
if( ret == NULL || !ret->construct() )
{
delete ret;
ret = NULL;
}
return ret;
}
void CalculatorUI::show()
{
this->QWidget::show();
this->setFixedSize(this->width(), this->height());
}
void CalculatorUI::onButtonClicked()
{
QPushButton* btn = (QPushButton*)sender(); // Returns the connection that sent the signal
QString clickText = btn->text();
if(clickText == "<-")
{
QString text = m_edit->text();
if(text.length() > 0)
{
text.remove(text.length()-1, 1);
m_edit->setText(text);
}
}
else if(clickText == "C")
{
m_edit->setText("");
}
else if(clickText == "=")
{
// 下节编写
}
else
{
m_edit->setText(m_edit->text() + clickText);
}
}
CalculatorUI::~CalculatorUI()
{
}
main.cpp
#include
#include "QCalculatorUI.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QCalculatorUI* cal = QCalculatorUI::NewInstance();
int ret = -1;
if( cal != NULL )
{
cal->show();
ret = a.exec();
delete cal;
}
return ret;
}
解决经典问题: Object::connect: No such slot… 、
1. 检查类是否继承于QObject
2. 检查类声明的开始处是否添加Q_OB丿ECT
3. 检查是否使用slots关键字进行槽声明
4. 检查槽的名称是否拼写错误
5. 重新执行qmake
Qt中信号的本质
-信号只是一个特殊的成员函数声明 ,函数的返回值是 void 类型 ,函数只能声明不能定义
-信号必须使用 signals 关键字进行声明 ,函数的访问属性自动被设置为protected ,只能通过 emit 关键字发射信号
信号定义示例
信号与槽的对应关系
-一个信号可以连接到多个槽(一对多),多个信号可以连接到一个槽(多对一)
-一个信号可以连接到另一个信号(转嫁)
-连接可以被 disconnect 函数删除(移除)
TestSignal.h
#include
class TestSignal : public QObject
{
Q_OBJECT
public:
void send(int i)
{
emit testSignal(i);
}
signals:
void testSignal(int v);
};
RxClass.h
#include
#include
class RxClass : public QObject
{
Q_OBJECT
protected slots:
void mySlot(int v)
{
qDebug() << "void mySlot(int v)";
qDebug() << "Sender: " << sender()->objectName();
qDebug() << "Receiver: " << this->objectName();
qDebug() << "Value: " << v;
qDebug() << endl;
}
};
main.cpp
#include
#include
#include "TestSignal.h"
#include "RxClass.h"
void emit_signal()
{
qDebug() << "emit_signal()" << endl;
TestSignal t;
RxClass r;
t.setObjectName("t"); //设置发送信号对象的对象名
r.setObjectName("r");
//信号连接到一个槽函数
QObject::connect(&t, SIGNAL(testSignal(int)), &r, SLOT(mySlot(int)));
for(int i=0; i<3; i++)
{
t.send(i);
}
}
void one_to_multi()
{
qDebug() << "one_to_multi()" << endl;
TestSignal t;
RxClass r1;
RxClass r2;
t.setObjectName("t");
r1.setObjectName("r1");
r2.setObjectName("r2");
//信号连接到两个槽函数
QObject::connect(&t, SIGNAL(testSignal(int)), &r1, SLOT(mySlot(int)));
QObject::connect(&t, SIGNAL(testSignal(int)), &r2, SLOT(mySlot(int)));
t.send(100);
}
void multi_to_one()
{
qDebug() << "multi_to_one()" << endl;
TestSignal t1;
TestSignal t2;
RxClass r;
t1.setObjectName("t1");
t2.setObjectName("t2");
r.setObjectName("r");
//多个信号连接到同一个槽函数
QObject::connect(&t1, SIGNAL(testSignal(int)), &r, SLOT(mySlot(int)));
QObject::connect(&t2, SIGNAL(testSignal(int)), &r, SLOT(mySlot(int)));
t1.send(101);
t2.send(102);
}
void signal_to_signal()
{
qDebug() << "signal_to_signal()" << endl;
TestSignal t1;
TestSignal t2;
RxClass r;
t1.setObjectName("t1");
t2.setObjectName("t2");
r.setObjectName("r");
//信号到信号的映射
QObject::connect(&t1, SIGNAL(testSignal(int)), &t2, SIGNAL(testSignal(int)));
QObject::connect(&t2, SIGNAL(testSignal(int)), &r, SLOT(mySlot(int)));
t1.send(101);//t2接收t1的信号,接着将t2发送信号到槽,值为101,发送者t2
t2.send(102);//然后t2再发出自己的信号,值102
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// emit_signal();
// one_to_multi();
// multi_to_one();
// signal_to_signal();
return a.exec();
}
依次调用以上函数
不可忽视的军规
1. Qt类只能在头文件中声明
2. 信号与槽的原型应该完全相同
3. 信号参数多于槽参数时,多于的参数被忽略
4. 槽函数的返回值必须是void类型
5. 槽函数可以像普通成员函数一样被调用
6. 信号与槽的访问属性对于connect / disconnect无效
信号与槽的意义
-最大限度的弱化了类之间的耦合关系
-在设计阶段,可以减少不必要的接口类(抽象类)
-在开发阶段,对象间的交互通过信号与槽动态绑定