在 QT里面实现控件与控件之间的联动关系(数据交互关系)
信号:
当一个组件发生任何改变(状态或者属性改变,人为主动操作/被动改变)
这些状态改变,称为 发生了事件
当任意一个组件,发生事件,该组件会发出一个与该事件关联的信号
通过发出不同的信号,来告诉外部,自己发什么了什么改变
本质是一个函数,必须是一个控件/窗口的成员函数
槽函数:
接受并处理一个信号
槽函数允许与一个信号关联
如果槽函数发现与其关联的信号触发之后,就会自动调用槽函数
种类:
1:槽函数可以是一个控件/窗口的成员函数
2:槽函数可以是一个全局函数
3:槽函数可以是一个匿名函数
4:槽函数可以是一个信号函数
关联
1.当槽函数是控件/窗口的成员函数
QObject::connect(信号发送方控件地址,&信号发送方控件类型::信号名,信号接收方地址,&信号接收方控件类型::槽函数名)
le = new QLineEdit();
btn = new QPushButton();
QObject::connect(btn,&QPushButton::clicked,le,&QLineEdit::setText)
槽函数如果带参数,实参来自于哪
信号和槽,不仅有简单的通知功能,还可传递数据
信号函数: signal(const QString& str = "hello world")
槽函数:slot(QString ptr);
signal 函数 和 slot 关联
那么,当信号函数触发, signal函数会将他的参数 str (默认为"hello world") 传递给 slot 的参数 ptr
所以注意:槽函数如果参数,说明要接受一个数据,那么信号就必须有一个相同的数据发过去,也就是信号函数必须有一个同样的参数
反过来不一样,信号如果有参数,槽函数可以没有
关联位置
在代码的任意处连接信号和槽
取决于,哪块位置访问需要连接的2个控件地址更为方便, 则就在哪里进行连接
2. 当槽函数是全局函数
void func(){qDebug() << "hello world";
}Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QObject::connect(ui->pushButton_2,&QPushButton::clicked,func);
使用 connect 函数直接与全局函数连接
}
3.当槽函数是lambda表达式
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QObject::connect(ui->pushButton_2,&QPushButton::clicked,[&]()mutable{qDebug() << "lambda";});
}
connect函数的第3个参数,直接写lambda表达式的定义就好了
4.当槽函数是信号
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QObject::connect(this,&Widget::mysignal,this,&Widget::myslot_2);QObject::connect(ui->pushButton_2,&QPushButton::clicked,this,&Widget::mysignal);
}
通过connect函数将 clicked 信号链接到 mysignal信号
由于mysignal信号连接到了 myslot_2,
所以,最终结果:clicked信号通过mysignal信号连接到了 myslot_2
自定义槽函数
目的
接受一个信号的槽函数,需要操作多个组件窗口,
拥有多个组件窗口地址的,一般就是一个父组件
so要为我们自己写的父组件,写一个自定义的槽函数,去操作多个组件
注意:
1:必须写在 public slots 访问权限里面
2:拥有自定义槽函数的类,必须在开头有一个宏:Q_OBJECT
如果没有,虚表报错
虚表报错了,再加上这个宏,编译还是报错,只能 重新构建 才能解决
3:有自定义槽函数的类,必须分文件编译
C++ 分文件的规则:一个类占一个头文件;
类里成员函数,定义在对应的.cpp 文件
练习:
编写2个聊天窗口,实现互相聊天功能
//widget.h
class Widget : public QWidget
{ Q_OBJECTQLineEdit* le;QTextEdit* te;QPushButton* btn;QWidget* sw;QHBoxLayout* hlay;QVBoxLayout* vlay;Widget*that;//public:Widget(QWidget *parent = nullptr);void setThat(Widget*w);///
public slots:void myslot();
signals:void mysignal(QString text);
};//widget.cpp
Widget::Widget(QWidget *parent) : QWidget(parent)
{sw = new QWidget(this); // 创建一个子窗口te = new QTextEdit(this); // 创建文本编辑器vlay = new QVBoxLayout(this); // 创建垂直布局(主布局)// 将子窗口和文本编辑器添加到主布局vlay->addWidget(sw);vlay->addWidget(te);le = new QLineEdit(sw); // 创建单行文本输入框btn = new QPushButton("Click Me", sw); // 创建按钮hlay = new QHBoxLayout(sw); // 创建水平布局(子窗口布局)// 将单行文本输入框和按钮添加到子窗口布局hlay->addWidget(le);hlay->addWidget(btn);// 连接按钮点击信号到槽函数connect(btn, &QPushButton::clicked, this, &Widget::myslot);}
void Widget::myslot(){QString text = le->text(); // 获取单行文本输入框的内容te->setPlainText(text); // 将内容显示在文本编辑器中te->append(text);le->clear();that->te->append(text);
}
void Widget::setThat(Widget*w){//that=w;
}//main.cpp
int main(int argc, char *argv[])
{QApplication app(argc, argv);Widget w1,w2;w1.setWindowTitle("Window 1");w2.setWindowTitle("Window 2");w1.setThat(&w2);w2.setThat(&w1);w1.show();w2.show();return app.exec(); // 启动事件循环
}
//widget.h
class Widget : public QWidget
{ Q_OBJECTQLineEdit* le;QTextEdit* te;QPushButton* btn;QWidget* sw;QHBoxLayout* hlay;QVBoxLayout* vlay;//Widget*that;
public:Widget(QWidget *parent = nullptr);// void setThat(Widget*w);void receiveText(const QString& text);
public slots:void myslot();
signals:void mysignal(QString text);
};//widget.cpp
Widget::Widget(QWidget *parent) : QWidget(parent)
{sw = new QWidget(this); // 创建一个子窗口te = new QTextEdit(this); // 创建文本编辑器vlay = new QVBoxLayout(this); // 创建垂直布局(主布局)// 将子窗口和文本编辑器添加到主布局vlay->addWidget(sw);vlay->addWidget(te);le = new QLineEdit(sw); // 创建单行文本输入框btn = new QPushButton("Click Me", sw); // 创建按钮hlay = new QHBoxLayout(sw); // 创建水平布局(子窗口布局)// 将单行文本输入框和按钮添加到子窗口布局hlay->addWidget(le);hlay->addWidget(btn);// 连接按钮点击信号到槽函数connect(btn, &QPushButton::clicked, this, &Widget::myslot);}
void Widget::myslot(){QString text = le->text(); // 获取单行文本输入框的内容//te->setPlainText(text); // 将内容显示在文本编辑器中te->append(text);le->clear();//that->te->append(text);emit mysignal(text);
}
//void Widget::setThat(Widget*w){
// that=w;
//}
void Widget::receiveText(const QString& text)
{te->setPlainText(text); // 将接收到的内容显示在文本编辑器中
}//main.cpp
int main(int argc, char *argv[])
{QApplication app(argc, argv);Widget w1,w2;w1.setWindowTitle("Window 1");w2.setWindowTitle("Window 2");// w1.setThat(&w2);// w2.setThat(&w1);// 连接窗口2的textSent信号到窗口1的receiveText槽函数QObject::connect(&w1, &Widget::mysignal, &w2, &Widget::receiveText);QObject::connect(&w2, &Widget::mysignal, &w1, &Widget::receiveText);w1.show();w2.show();return app.exec(); // 启动事件循环
}
自定义信号
目的:
省写一个接口
接口和信号做的是一件事情
怎么写
1:写在signals访问权限里面
2:参数,也可向槽函数发送数据,所以槽函数要什么数据,自定义信号写什么数据就好了
3:信号函数无需定义
4:发送信号时,直接调用信号函数,函数前加 emit
转到槽 功能
依赖于ui界面,ui界面中的子组件,通过ui->去访问
只适用于能够在ui界面上显示的组件
一些组件无法在ui界面上布局,只能用 connect
connect 最后一个参数的意义
Qt::AutoConnection自动连接:
如果信号和槽所在的组件,处于同一个线程,则使用Qt::DirectConnection模式,如果不处于同一个线程,则使用Qt::QueuedConnection
Qt::DirectConnection 直接连接:
当信号和槽所在的组件,处于同一个线程的时候,信号一旦发送,则立刻调用槽函数
Qt::QueuedConnection队列连接:
当信号和槽所在的组件,不处于同一个线程的时候,信号触发后,需要等待程序运行到槽函数所在的组件所处的线程运行的时候,才会触发槽函数
Qt::BlockingQueuedConnection 阻塞队列连接:
当信号和槽所在的组件,不处于同一个线程的时候,信号触发后,需要等待程序运行到槽函数所在的组件所处的线程运行的时候,才会触发槽函数
但是 信号所在的线程会阻塞,直到槽函数运行完才接触阻塞