为什么局域网做网站,南通网站建设排名,站长工具网站备案查询,自己做图网站一、信号和槽概述 信号槽是Qt矿建引以为豪的机制之一。 所谓信号槽#xff0c;实际上就是观察者模式#xff08;发布——订阅模式#xff09;。当某个事件发生之后#xff0c;比如#xff0c;按钮检测到自己被点击了一下#xff0c;它就会发出一个信号。这种发出的信号是…一、信号和槽概述 信号槽是Qt矿建引以为豪的机制之一。 所谓信号槽实际上就是观察者模式发布——订阅模式。当某个事件发生之后比如按钮检测到自己被点击了一下它就会发出一个信号。这种发出的信号是没有目的的类似于广播。如果有对象对这个信号感兴趣它就会使用连接函数connect)意思是将想要处理的信号和自己的一个函数称为槽绑定来处理这个信号。也就是说当信号发出时被连接的槽函数会自定被回调。 这就类似于观察者模式当发生了感兴趣的事件某一个操作就会被自动触发。
1.1 信号的本质 信号是由于用户对窗口或控件进行了某些操作导致窗口或控件产生了某个特定事件这时候Qt对应的窗口类会发出某个信号对用以此户的挑选做出反应。
根据上面的论述我们可以得出一个结论——信号的本质就是事件例如
按钮单击、双击窗口刷新鼠标移动、鼠标按下、鼠标释放键盘输入
在Qt中信号是通过什么形式呈现给使用者的呢
我们对哪个窗口进行操作哪个窗口就可以捕捉到这些被触发的事件对于使用者来说触发一个事件我们就可以得到Qt框架给我们发出的某个特定信号信号的呈现形式就是函数也就是说某个事件产生了Qt框架就会调用某个对应的信号函数通知使用者。 在Qt中信号的发出者是某个实例化的类对象对象内部可以进行相关事件的检测。
1.2 槽的本质 在Qt中槽函数是一类特殊功能的函数在编码过程中也可以作为类的普通成员函数来使用。之所以称之为槽函数是因为它们还有一个职责就是对Qt框架中产生的信号进行处理。 举个例子 有一天我们在和女朋友一起逛街突然女朋友说“我肚子饿了”于是我们带着她们去吃饭。这就相当于女朋友发出一个信号我收到信号并将其处理掉 实例对象角色描述女朋友信号发出者信号携带的信息我饿了我信号接收者处理女朋友发射的信号带她去吃饭 在Qt中槽函数的所有者也是某个类的实例对象。
1.3 信号和槽的关系
在Qt中信号和槽函数都是独立的个体本身没有任何联系但是由于某种特性需求我们可以将二者连接在一起好比牛郎和织女想要见面必须要有喜鹊为他们搭桥。
在Qt中我们需要使用QObject类中的connect函数进行两者的关联。
连接信号和槽的connect()函数原型如下其中PointerToMenberFunction是一个指向函数地址的指针
QMetaObject::Connection QObject::connect(const QObject* sender, PointerToMemberFunction signal,const QObject* receiver, PointerToMenberFunction method,Qt::ConnectionType type Qt::AutoConnection);// 参数
// sender:发出信号的对象
// signal:属于sender对象信号是一个函数这个参数的类型是函数指针信号函数地址
// receiver:信号接受者
// method:属于receiver对象当检测到sender发出了signal信号receiver对象调用method方法信号发出之后的处理动作// 参数signal和method都是函数地址因此简化后的connect()如下
connect(const QObject* sender, QObject::signal,const QObject* receiver, QObject::method);
使用connect()函数进行信号槽的连接的注意事项
connect函数相对于做了信号处理动作的注册调用connect函数的sender对象的信号没有产生因此receiver对象的method也不会被调用method槽函数本质是一个回调函数调用的时机是信号产生之后调用的是Qt框架来执行的connect连接中的sender和receiver两个指针必须被实例化否则connect不会成功
二、标准信号槽的使用
2.1 标准信号/槽 在Qt中提供了很多标准类中都可以对用户触发的某种特定事件进行检测因此当用户做了这些操作之后事件被触发类的内部就会产生对应的信号这些信号都是Qt类内部自带的因此称之为标准信号。
同样地在Qt的很多类的内部中为我提供了很多功能函数并且这些函数也可以作为触发信号的处理动作有这类特性的函数在Qt中称之为标准槽函数。
系统自带的信号和槽通常如何查找这个就需要利用帮助文档比如在帮助文档中查询按钮的点击信号那么需要在帮助文档中输入QPushButton
2.2 使用 举个例子 功能实现点击窗口上的按钮关闭窗口 功能分析按钮信号发出者 QPushButton 类型 窗口信号的接收者和处理处 QWidget 类型 // 需要使用的标准信号槽函数
// 单击按钮发出的信号
void QAbstractButton::clicked(bool check false);
// 关闭窗口的槽函数
bool QWidget::close();// 点击按钮关闭窗口
connect(ui-closewindow, QPushButton::clicked, this, MainWindow::close); connect()操作一般写在窗口的构造函数汇总相当于在事件产生之前在Qt框架中先进行注册这样在程序运行过程中假设产生了按钮的点击事件框架就会调用信号接收者对象对应的槽函数如果信号不产生槽函数也就一直不会被调用。
三、自定义信号槽的使用 Qt框架提供的信号槽在某种特定的场景下是无法满足我们的项目的要求因此我们还设计自己需要的信号和槽同样还是使用connect()对自定义的信号槽进行连接。
如果想要在Qt类中自定义信号槽需要满足一些条件并且也要注意一些事情
要编写新的类并且让其继承Qt的某些标准类这个新的子类必须从QObject类或者是QObject子类进行派生在定义类的头文件中假如Q_OBJECT宏
// 在头文件派生类的时候首先像下面那样引入Q_OBJECT宏class MyMainWindow : public QWidget
{Q_OBJECT......
}
3.1 自定义信号 在Qt中信号的本质是事件但是在框架中也是以函数的形式存在的只不过信号对应的函数只有声明没有定义。如果Qt中的标准信号不能满足我们的需求可以在程序中进行信号的自定义当自定义信号对应的时间产生之后认为将这个信号发射出去即可其实就是调用一下这个信号函数
自定义信号的要求和注意事项
信号是类的成员函数返回值必须是void类型信号的名字可以根据实际情况进行指定参数可以随意指定信号也支持重载信号需要使用signals关键字进行声明使用方法类似于public等关键字信号函数只需要声明不需要定义没有函数实现在程序中发射自定义信号发送信号的本质就是调用信号函数。习惯性在信号函数前加关键字emit但是可以省略不写emit只是显示的声明一下信号要被发射了没有特殊含义底层中 emit #define emit
class Test : public Qobject
{Q_OBJECT
signals:void testsignals();// 参数的作用是数据传递谁调用信号函数谁就指定实参// 实参最终会被传递给槽函数void testsignals(int a);
}
3.2 自定义槽 槽函数就是信号的处理动作在Qt中槽函数可以作为普通的成员函数来使用。如果标准槽函数提供的功能满足不了需求可以自己定义槽函数进行某些特殊功能的实现。自定义槽函数和自定义的普通函数写法是一样的。
自定义槽的要求和注意事项
返回值必须是void类型槽也是函数因此也支持重载槽函数需要指定多少个参数需要看连接信号的参数个数连接信号的参数个数大于槽函数的参数个数槽函数的参数是用来接收信号传递的数据信号传递的数据就是信号的参数Qt中的槽函数类型是多样的Qt中的槽函数可以是类的成员函数全局函数静态函数Lambda表达式匿名函数槽函数可以使用关键字进行声明slotsQt5中slots可以省略不写 再来通过一个例子来自定义一个信号槽还是上面的场景女朋友说“我肚子饿了”于是我带她去吃饭 class GirlFriend : public Object
{Q_OBJECT
public:explicit GirlFriend(QObject* parent nullptr);signals:void hungry();void hungry(QString msg);
};class Me : public Object
{Q_OBJECT
public:explicit Me(QObject* parent nullptr);public slots:// 槽函数void eatMeal();void eatMeal(QString msg);
}; 四、信号槽的扩展
4.1 信号槽使用扩展
一个信号可以连接多个槽函数发送一个信号有多个处理动作
需要写多个connect()连接槽函数的执行顺序是随机的和connect函数的调用顺序没有关系信号的接收者可以是一个对象也可以是多个对象
一个槽函数可以连接多个信号多个不同的信号处理动作是相同的
需要写多个connect连接
信号可以连接信号
信号接收者可以不处理接收的信号而是继续发射新的信号这就相当于传递了数据并没有随数据进行处理 connect(const QObject* sender, QObject::singal,const QObject* receiver, QObject::signal_new);
信号槽是可以通过函数断开的 disconnect(const QObject* sender, QObject::signal,const QObject* receiver, QObject::method);
4.2 信号槽的连接方式
4.2.1 Qt5的连接方式
// 语法
QMetaObject::Connection QObject::connect(const QObject* sender, PointerToMenmberFunction signal,const QObject* receiver, PointerToMemberFunction method,Qt::ConnectionType Qt::AutoConnection);// 信号和槽函数也就是第2,4个参数传递的是地址编译器在编译过程中会对数据的正确性进行检查
connect(const QObject* sender, QObject::signal, const QObject* receiver, QObject::method);
4.2.2 Qt4的连接方式 这种旧的信号槽连接方式在Qt5中是支持的但是不推荐使用因为这种方式在进行信号槽的连接的时候信号槽函数通过宏SIGNAL和SLOT转换为字符串类型。 因为信号槽函数的 转换是通过宏来进行转换的因此传递到宏函数内部的数据不会被进行检测如果使用者传错了数据编译器不会报错但实际上信号槽的连接已经不对了只有在程序运行起来之后才能发现问题而且问题不容易被定位。
4.2.3 Qt5处理方式错误的原因 上边的写法之所以错误是因为这个类中信号槽都是重载过的, 信号和槽都是通过函数名去关联函数的地址, 但是这个同名函数对应两块不同的地址, 一个带参, 一个不带参, 因此编译器就不知道去关联哪块地址了, 所以如果我们在这种时候通过以上方式进行信号槽连接, 编译器就会报错。
4.2.4 总结
Qt4的信号槽连接方式因为使用了宏函数, 宏函数对用户传递的信号槽不会做错误检测, 容易出bugQt5的信号槽连接方式, 传递的是信号槽函数的地址, 编译器会做错误检测, 减少了bug的产生当信号槽函数被重载之后, Qt4的信号槽连接方式不受影响当信号槽函数被重载之后, Qt5中需要给被重载的信号或者槽定义函数指针
五、Lambda表达式
5.1 语法格式
Lambda表达式就是一个匿名函数语法格式如下
[capture](params) opt- ret{ body; };--capture:捕获列表--params:参数列表--opt:函数选项--ret:返回值类型--body:函数体
5.1.1 捕获列表捕获一定范围内的变量
[ ]不捕捉任何变量[]捕获外部作用域中的所有变量并作为引用在函数体内使用按引用捕获[]捕获外部作用域中的所有变量 并作为副本在函数体内使用按值捕获拷贝的副本在匿名函数体内是只读的[ , foo]按值捕获外部作用域中的所有变量并按照引用捕获外部变量foo[bar]按值捕获bar变量同时不捕获其他变量[bar]按引用捕获bar变量同时不捕获其他变量[this]捕获当前类中的this指针让lambda表达式拥有和当前成员函数同样的访问权限如果已经使用了或者默认添加次选项
5.1.2 参数列表和普通函数的参数列表一样
5.1.3 opt选项 - 可以省略
mutable可以修改按值传递进来的拷贝注意是能修改拷贝而不是值本身execption指定函数抛出的异常如抛出整数类型的异常可以使用throw()
5.1.4 返回值类型 表示函数返回值的类型当返回值为void或者函数体中只有溢出return的地方此时编译器可以自动推断出返回值的类型时这部分可以省略。
5.1.5 函数体 函数的实现这部分不能省略但是函数体可以为空。
5.2 定义和调用 因为Lambda表达式是一个匿名函数因此是没有函数声明的直接在程序中进行代码的定义即可但是如果只定义匿名函数在程序执行过程中是不糊被调用的。
// 匿名函数的定义程序执行这个匿名函数是不会被调用的
[]() {qDebug() Hello, I am lambda...;
};// 匿名函数的定义 调用
int ret [](int a) - int
{return a 1;
}(100); // 100是传递给匿名函数的参数在Lambda表达式中的捕获列表中也就是[ ] 中添加不同的关键字就可以在函数体中使用外部变量了。
// 在匿名函数外部定义变量
int a 100, b 200, c 300;// 调用匿名函数
// 使用引用的方式传递数据
[](){qDebug() a 1: a , b c b c;
}();// 值拷贝的方式使用外部数据
[](int m, int n) mutable{qDebug() Hello, 我是一个lambda表达式...;qDebug() 使用拷贝的方式传递数据:;// 拷贝的外部数据在函数体内部是只读的如果不添加mutable关键字是不能修改这些只读数据的值// 添加mutable允许修改的数据是拷贝到函数内部的副本对外部数据没有影响qDebug() a 1: a , b c b c;qDebug() m 1: m , n: n;
}(1, 2);