界面编程之QT的信号与槽20180725
/*******************************************************************************************/
一、指定父对象
/*如果不指定父对象,对象和对象(窗口和窗口)没有关系,独立
* a指定b为它的父对象,a放在b的上面
* 指定父对象,有2种方式:
* 1)setParent
* 2)通过构造函数传参
* 指定父对象,只需要父对象显示,上面的子对象自动显示,不需要再去手动去show子对象了
*/
1)setParent
QPushButton b;
b.setText("^_^"); //给按钮设置内容,
b.setParent(&w); //指定父对象,如果不指定父对象,这里直接show,那么显示的是两个独立的窗口
b.move(100, 100); //移动坐标,以像素点为单位,以左上角为基准点,如果没有移动,默认位置是在左上角
2)通过构造函数传参
QPushButton b1(&w); //通过构造函数传参
b1.setText("abc");
w.show();
//窗口也是一种控件,按钮也是一种控件,所以这种继承关系就导致了一种叫法,窗口是父对象,同时也是父控件,父对象,
//对应,按钮是子对象,同时也是子控件,子窗口
/*******************************************************************************************/
二、标准信号和槽
正常新建工程都是选择application模版中的qt widgets application
同样需要去掉创建界面的勾选框,继承Qwidget类
1.信号和槽的介绍
信号槽是 Qt 框架引以为豪的机制之一。所谓信号槽,实际就是观察者模式。当某个事件发生之后,比如,
按钮检测到自己被点击了一下,它就会发出一个信号(signal)。这种发出是没有目的的,类似广播。
如果有对象对这个信号感兴趣,它就会使用连接(connect)函数,意思是,
将想要处理的信号和自己的一个函数(称为槽(slot))绑定来处理这个信号。也就是说,当信号发出时,被连接的槽函数会自动被回调。
这就类似观察者模式:当发生了感兴趣的事件,某一个操作就会被自动触发。(这里提一句,Qt 的信号槽使用了额外的处理来实现,
并不是 GoF 经典的观察者模式的实现方式。)
2.信号的含义
所谓信号,就是点击按钮或其他控件后,会产生软中断,产生后会广播发送出信号,如果有谁感兴趣的就会去处理这个信号,
那么如何才能表示感兴趣,就是要建立连接关系,那么这个信号发生的时候,就会自动的调用所连接的函数
也就是说如果对某信号感兴趣,就要有函数和这个信号建立联系,那么这个信号发生的时候,这个函数就会被调用
3.信号相关的函数
建立连接关系函数:
connect(&b1, &QPushButton::pressed, this, &MainWidget::close);
第一个参数是发出信号的控件地址,
第二参数表示的是这个控件发出的是哪个信号,具体这个信号填什么需要看帮助文档,
即光标放在按钮类名上按f1就可以出现帮助文档,再f1全屏,在其中找是否有signal或者再到其父类中找signal
第三个参数是信号的接收者,表示的是谁来接收这个信号,也就是谁感兴趣,即信号的处理函数所属于的类的对象,
当接收者接收到对应的信号(第二个参数)就会自动去调用信号的处理函数,即第四个参数
第四个参数是该信号的处理函数(槽函数),也就是接收到信号要调用的函数,这个函数是第三个参数的成员函数
/* &b1: 信号发出者,指针类型
* &QPushButton::pressed:处理的信号, &发送者的类名::信号名字
* this: 信号接收者
* &MainWidget::close: 槽函数,信号处理函数 &接收的类名::槽函数名字
*/
在成员函数上按f1也会有对应的帮助文档,在帮助文档中可以看到MainWidget::close函数标注了是槽函数,也就是
QPushButton::pressed是标准的信号,MainWidget::close是标准的槽函数。
/*******************************************************************************************/
三、自定义槽函数
/* 自定义槽,就是普通函数的用法
* Qt5中,自定义槽可以是:任意的成员函数,普通全局函数,静态函数
* 槽函数需要和信号一致(指的是参数,返回值一致),所以
* 由于信号都是没有返回值,所以,槽函数一定没有返回值
*/
connect(b2, &QPushButton::released, this, &MainWidget::mySlot);
//一个信号可以有多个处理 /* 可以打比喻,信号:短信 槽函数:接收短信的手机,一条短信可以发给多个手机*/
connect(b2, &QPushButton::released, &b1, &QPushButton::hide);
/*******************************************************************************************/
四、两个独立的窗口
添加新的窗口,就需要添加一个新的类,添加的方法是选择添加c++ class 具体见图1:
/*******************************************************************************************/
五、自定义信号
1.自定义信号
//(自)定义信号要在signals关键字后面,signals是qt特有的关键字,编译的时候qt会把它转换到g++可以编译的
signals:
/* 信号必须有signals关键字来声明
* 信号没有返回值,但可以有参数
* 信号就是函数的声明,只需声明,无需定义
* 使用:emit mySignal();,即发送信号使用emit关键字
* 信号可以重载
*/
void mySignal();
void mySignal(int, QString);
2.发送信号
//发送信号使用emit关键字
connect(&b, &QPushButton::clicked, this, &SubWidget::sendSlot);//把信号与信号对应的处理函数(槽函数)建立起连接关系
resize(400, 300);//如果不resize,则每次切换的时候窗口大小会变
void SubWidget::sendSlot()
{
emit mySignal();
emit mySignal(250, "我是子窗口");
}
//处理子窗口的信号
connect(&w, &SubWidget::mySignal, this, &MainWidget::dealSub);
resize(400, 300);//如果不resize,则每次切换的时候窗口大小会变
3.注意
qt中用别人的代码时候,注意要去掉.user文件,否则可能编译不过
对话框的特点是不能伸缩的,只有一个x按钮
信号与槽:是qt对象之间通信的接口
/*******************************************************************************************/
六、带参数的信号
1.定义带参数的信号
signals:
/* 信号必须有signals关键字来声明
* 信号没有返回值,但可以有参数
* 信号就是函数的声明,只需声明,无需定义
* 使用:emit mySignal();
* 信号可以重载
*/
void mySignal();
void mySignal(int, QString);
2.发送带参数的信号
void SubWidget::sendSlot()
{
emit mySignal();
emit mySignal(250, "我是子窗口");
}
3.信号的重载
1).qt5的处理方法
//当信号出现重载,为了区分具体是属于哪一个,就需要用到函数指针,定义函数指针等于具体哪一个信号。同样如果槽函数重载也得这么转换
void (SubWidget::*funSignal)() = &SubWidget::mySignal;
connect(&subWin, funSignal, this, &MainWidget::dealSub);
void (SubWidget::*testSignal)(int, QString) = &SubWidget::mySignal;
connect(&subWin, testSignal, this, &MainWidget::dealSlot);
2).qt4的处理方法
//还有另外一种更方便的方法,只不过容易出问题,也就是接下来要说的:Qt4信号连接。实现功能是一样的
//Qt4信号连接使用宏:SIGNAL。Qt4槽函数必须有slots关键字来修饰,然后再使用宏SLOT
connect(&subWin, SIGNAL(mySignal()), this, SLOT(dealSub()) );
connect(&subWin, SIGNAL(mySignal(int,QString)),
this, SLOT(dealSlot(int,QString)) );
// 容易出错的地方:SIGNAL SLOT 将函数名字 -> 字符串 不进行错误检查。即写错了信号名字等,编译的时候不报错。所以尽可能不用这种qt4的方式
//Qt4槽函数必须要有的关键字
public slots:
void mySlot();
void changeWin();
void dealSub();
void dealSlot(int, QString);
void MainWidget::dealSlot(int a, QString str)
{
// str.toUtf8() -> 字节数组QByteArray
// ……data() -> QByteArray -> char *
qDebug() << a << str.toUtf8().data();//qDebug qt中的打印,类似cout,必须#include <QDebug>
}
4.信号槽的更多用法
1).一个信号可以和多个槽相连
如果是这种情况,这些槽会一个接一个的被调用,但是它们的调用顺序是不确定的。
2).多个信号可以连接到一个槽
只要任意一个信号发出,这个槽就会被调用。
3).一个信号可以连接到另外的一个信号
当第一个信号发出时,第二个信号被发出。除此之外,这种信号-信号的形式和信号-槽的形式没有什么区别。
4).槽可以被取消链接
这种情况并不经常出现,因为当一个对象delete之后,Qt自动取消所有连接到这个对象上面的槽。
具体见图2,3
/*******************************************************************************************/
七、LamDa表达式和再说信号的功能
//Lambda表达式(匿名函数对象)
//C++11增加的新特性, 因为是新增加的所以需要在项目文件(.pro文件): CONFIG += C++11
//在Qt中,配合信号一起使用,非常方便。方便在不用定义槽函数,不用指定接收者
1.Lambda表达式(匿名函数对象)
Lambda表达式:
[]()
{
}
//类似于函数只不过没有函数名与返回值,然后多了个[],表示函数的开始
//其中[]内部可以放Lambda表达式外面的变量,这样Lambda表达式里的函数体内才可以使用,例如:
QPushButton *b4 = new QPushButton(this);
int a = 10, b = 100;
[b4,a,b]()
{//这样内部才可以使用b4,a,b,不过对于{}内部来说是只读的,{}内部不能修改这些变量
}
//如果要把外部所有局部变量、类中所有成员都以值传递方式(传递进来是只读的,表达式内不能修改,除非
在()后面加关键字mutable : [=]() mutable{})传递到Lambda表达式里的函数体内,则在[]中加入等号=
//如果要把类中所有成员以值传递方式传递到Lambda表达式里的函数体内,则在[]中加入this
//如果要把外部所有局部变量传递到Lambda表达式里的函数体内,则在[]中加入引用符号& 。
注意&尽量少用,如下:
int a = 10, b = 100;
connect(b4, &QPushButton::clicked,
[&]()
{
qDebug() << a<< b;//内存还没释放(a,b,b4之类的内存还在使用,可能相互影响),引用的内存不是局部变量的,就会出错
a=11;
}
);
2.Lambda表达式在信号中的使用
Lambda表达式直接代替掉接收者和槽函数,信号产生后,直接执行Lambda表达式
如果信号有参数的情形:
//clicked 信号有参数 bool checked =false,即点击后值变为false
connect(b4, &QPushButton::clicked,
// = :把外部所有局部变量、类中所有成员以值传递方式
// this: 类中所有成员以值传递方式
// & : 把外部所有局部变量, 引用符号
[=](bool isCheck)
{
qDebug() << isCheck;//这样就接收到了信号发出的参数
}
);
根据前面信号与槽的实现,点击按钮后窗口发生变化,窗口发生变化是按钮触发的,但是实质上
按钮只是发送了信号,然后槽函数被调用,真正引起的变化是槽函数做的,也就是成员函数做的。
同样点击按钮切换窗口时,按钮只是触发了软中断让槽函数被调用,然后是槽函数里面发出了信号。
也就是说具体做什么事情由槽函数决定
信号与槽相关的代码,具体见《SignalAndSlot》:
#ifndef MAINWIDGET_H
#define MAINWIDGET_H #include <QWidget>
#include <QPushButton>
#include "subwidget.h" //子窗口头文件 class MainWidget : public QWidget
{
Q_OBJECT public:
MainWidget(QWidget *parent = );
~MainWidget(); public slots:
void mySlot();
void changeWin();
void dealSub();
void dealSlot(int, QString); private:
QPushButton b1;
QPushButton *b2;
QPushButton b3; SubWidget subWin;
}; #endif // MAINWIDGET_H
mainwidget.h
#include "mainwidget.h"
#include <QPushButton>
#include <QDebug> //打印 MainWidget::MainWidget(QWidget *parent)
: QWidget(parent)
{
b1.setParent(this);
b1.setText("close");
b1.move(, ); b2 = new QPushButton(this);
b2->setText("abc"); connect(&b1, &QPushButton::pressed, this, &MainWidget::close);
/* &b1: 信号发出者,指针类型
* &QPushButton::pressed:处理的信号, &发送者的类名::信号名字
* this: 信号接收者
* &MainWidget::close: 槽函数,信号处理函数 &接收的类名::槽函数名字
*/ /* 自定义槽,普通函数的用法
* Qt5:任意的成员函数,普通全局函数,静态函数
* 槽函数需要和信号一致(参数,返回值)
* 由于信号都是没有返回值,所以,槽函数一定没有返回值
*/
connect(b2, &QPushButton::released, this, &MainWidget::mySlot); connect(b2, &QPushButton::released, &b1, &QPushButton::hide); /* 信号:短信
* 槽函数:接收短信的手机
*/ setWindowTitle("老大");
//this->setWindowTitle("老大"); b3.setParent(this);
b3.setText("切换到子窗口");
b3.move(, ); //显示子窗口
//subWin.show(); connect(&b3, &QPushButton::released, this, &MainWidget::changeWin); //处理子窗口的信号
// void (SubWidget::*funSignal)() = &SubWidget::mySignal;
// connect(&subWin, funSignal, this, &MainWidget::dealSub); // void (SubWidget::*testSignal)(int, QString) = &SubWidget::mySignal;
// connect(&subWin, testSignal, this, &MainWidget::dealSlot); //Qt4信号连接
//Qt4槽函数必须有slots关键字来修饰
connect(&subWin, SIGNAL(mySignal()), this, SLOT(dealSub()) ); connect(&subWin, SIGNAL(mySignal(int,QString)),
this, SLOT(dealSlot(int,QString)) );
// SIGNAL SLOT 将函数名字 -> 字符串 不进行错误检查 //Lambda表达式, 匿名函数对象
//C++11增加的新特性, 项目文件: CONFIG += C++11
//Qt配合信号一起使用,非常方便 QPushButton *b4 = new QPushButton(this);
b4->setText("Lambda表达式");
b4->move(, );
int a = , b = ;
connect(b4, &QPushButton::clicked,
// = :把外部所有局部变量、类中所有成员以值传递方式
// this: 类中所有成员以值传递方式
// & : 把外部所有局部变量, 引用符号
[=](bool isCheck)
{
qDebug() << isCheck;
} ); resize(, );
} void MainWidget::dealSlot(int a, QString str)
{
// str.toUtf8() -> 字节数组QByteArray
// ……data() -> QByteArray -> char *
qDebug() << a << str.toUtf8().data();
} void MainWidget::mySlot()
{
b2->setText("");
} void MainWidget::changeWin()
{
//子窗口显示
subWin.show();
//本窗口隐藏
this->hide();
} void MainWidget::dealSub()
{
//子窗口隐藏
subWin.hide();
//本窗口显示
show();
} MainWidget::~MainWidget()
{ }
mainwidget.cpp
#ifndef SUBWIDGET_H
#define SUBWIDGET_H #include <QWidget>
#include <QPushButton> class SubWidget : public QWidget
{
Q_OBJECT
public:
explicit SubWidget(QWidget *parent = ); void sendSlot(); signals:
/* 信号必须有signals关键字来声明
* 信号没有返回值,但可以有参数
* 信号就是函数的声明,只需声明,无需定义
* 使用:emit mySignal();
* 信号可以重载
*/ void mySignal();
void mySignal(int, QString); public slots: private:
QPushButton b;
}; #endif // SUBWIDGET_H
subwidget.h
#include "subwidget.h" SubWidget::SubWidget(QWidget *parent) : QWidget(parent)
{
this->setWindowTitle("小弟");
b.setParent(this);
b.setText("切换到主窗口"); connect(&b, &QPushButton::clicked, this, &SubWidget::sendSlot); resize(, );
} void SubWidget::sendSlot()
{
emit mySignal();
emit mySignal(, "我是子窗口");
}
subwidget.cpp
界面编程之QT的信号与槽20180725的更多相关文章
- 界面编程之QT的线程20180731
/*******************************************************************************************/ 一.为什么需 ...
- 界面编程之QT窗口系统20180726
/*******************************************************************************************/ 一.坐标系统 ...
- 界面编程之QT的基本介绍与使用20180722
/*******************************************************************************************/ 一.qt介绍 ...
- 界面编程之QT的Socket通信20180730
/*******************************************************************************************/ 一.linu ...
- 界面编程之QT的事件20180727
/*******************************************************************************************/ 一.事件 1 ...
- 界面编程之QT的数据库操作20180801
/*******************************************************************************************/ 一.数据库连 ...
- 界面编程之QT的文件操作20180729
/*******************************************************************************************/ 一.QT文件 ...
- 界面编程之QT绘图和绘图设备20180728
/*******************************************************************************************/ 一.绘图 整 ...
- QT核心编程之Qt线程 (c)
QT核心编程之Qt线程是本节要介绍的内容,QT核心编程我们要分几个部分来介绍,想参考更多内容,请看末尾的编辑推荐进行详细阅读,先来看本篇内容. Qt对线程提供了支持,它引入了一些基本与平台无关的线程类 ...
随机推荐
- 汇编 if else
知识点: if else 逆向还原代码 一.了解if else结构 sub esp, |. 8B45 FC MOV EAX,DWORD PTR SS:[EBP-] 0040102C |. 3B45 ...
- ubuntu 桌面操作系统安装WPS办公软件的方法
1.打开ubuntu系统自带的firefox软件 2.打开linux.wps.cn,并点击立即下载 3. 点击下载deb安装包 4.进入下载目录,sudo dpkg -i wps-office_10. ...
- Nginx安装负载均衡配置 fair check扩展
前言 本文主要是针对Nginx安装.负载均衡配置,以及fair智能选举.check后端节点检查扩展功能如何扩展,进行讲解说明. fair模块: upstream-fair,“公平的”Nginx 负载均 ...
- 镜像仓库管理:与Portus不得不说的那些事
背景: 目前在做一个云计算相关的项目,其中有这样一个需求:每个平台用户都有自己的docker镜像仓库(docker registry),用户可以对自己的镜像仓库的push/pull权限进行管理,也就是 ...
- python 游戏(猜数字)
1. 构造猜数字核心函数 import random def guess_core(guess_min,guess_max,guess_counrt): '''猜数字核心判断函数 :param gue ...
- 使用tomcat,不能连接localhost/8080的解决办法
首先,java的一些环境变量要解决. 其次,tomcat也应该各种环境变量设置好. 最后,把下图的那个地址重新选择一遍. 记住以上每一步弄好了之后都重启一下机器. 我也不知道为什么,但是有些就是从起之 ...
- Final发布 文案+美工展示
此作业要求参见:https://edu.cnblogs.com/campus/nenu/2018fall/homework/2476项目地址:https://coding.net/u/wuyy694/ ...
- 配置tomcat虚拟路径
这个问题其实很简单,首先找到你安装的tomcat的路径然后,在Tomcat6.0/conf/Catalina路径下创建localhost文件夹,如果有,就不用创建,然后在该文件夹下创建一个xml文件, ...
- BackBone及其实例探究
摘要 我们小组对MVC框架进行了学习.我的队友们已经在博客中对MVC的设计模式及优缺点进行了详细的探讨与分析,因此我的博客中只对MVC进行简单的介绍,而我将把重心放在Backbone MVC框架一 ...
- Linux内核分析——第五周学习笔记
第五周 扒开系统调用的“三层皮”(下) 一.知识点总结 (一)给MenuOS增加time和time-asm命令 在实验楼中,首先 强制删除menu (rm menu -rf) 重新克隆一个新版本的me ...