【Qt】Qt中的窗口坐标 信号与槽

embedded/2024/9/29 11:00:13/

Qt中的窗口坐标 && 信号与槽

  • 1. Qt中的窗口坐标
  • 2. 信号与槽的概述
  • 3. 信号和槽的使用
    • 3.1 connect函数的使用
    • 3.2 查看内置信号和槽
    • 3.2 connect的参数类型不匹配问题
  • 4. 自定义信号 && 自定义槽
    • 4.1 自定义槽
    • 4.2 自定义信号
  • 5. 带参数的信号和槽
  • 6. 信号与槽的关联方式
    • 6.1 一对一
    • 6.2 一对多 && 多对一
    • 6.3 多对多
  • 7. 信号与槽的断开
  • 8. 使用Lambda表达式定义槽
  • 9. 信号和槽的优缺点

1. Qt中的窗口坐标

在Qt中坐标系的开始是左上角(0,0),从在左上角往右依次增加的是x轴,从左上角往下依次增加的是y轴。

在这里插入图片描述

而在一个窗口中不止一个坐标系。对于嵌套窗口来讲,它的坐标是相对于父窗口来讲的

#include "widget.h"
#include "ui_widget.h"
#include <QPushButton>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);// 这个 button的父窗口就是WidgetQPushButton* button = new QPushButton(this);button->setText("按钮");button->move(200, 200); // 这个就是设置坐标的函数,单位是像素点// 而对于Widget来讲,它的坐标轴就是我们当前的电脑屏幕this->move(200, 0);
}Widget::~Widget()
{delete ui;
}

在这里插入图片描述

我们可以将鼠标对准move函数然后按F1可以快速的查看文档。

在这里插入图片描述

2. 信号与槽的概述

  • 在 Qt 中,用户和控件的每次交互过程称为⼀个事件。比如 "用户点击按钮"是⼀个事件,“用户关闭窗口” 也是⼀个事件。每个事件都会发出⼀个信号,例如用户点击按钮会发出 “按钮被点击” 的信号,用户关闭窗口会发出 “窗口被关闭” 的信号。

  • Qt 中的所有控件都具有接收信号的能力,⼀个控件还可以接收多个不同的信号。对于接收到的每个信号,控件都会做出相应的响应动作。例如,按钮所在的窗口接收到 “按钮被点击” 的信号后,会做出 “关闭自己” 的响应动作;在 Qt 中,对信号做出的响应动作就称之为槽。 信号和槽是 Qt 特有的消息传输机制,它能将相互独立的控件关联起来。比如,“按钮” 和 “窗口” 本⾝是两个独立的控件,点击 “按钮” 并不会对 “窗口” 造成任何影响。通过信号和槽机制,可以将 “按钮” 和 “窗口” 关联起来,实现 “点击按钮会使窗口关闭” 的效果。

信号的本质:

  • 信号是由于用户对窗口或者控件进行操作,导致窗口或者控件触发了某些事件,这个时候Qt对应的窗口类会发出特定的信号,来对用户的操作进行处理。所以信号的本质就是事件

  • 而在Qt中常见的事件右:点击按钮,窗口刷新,移动鼠标,点击鼠标,键盘输入……。

信号的传递方式:

  • 首先我们对哪个窗口进行操作,哪个窗口就可以捕捉到这些触发的事件。
  • 对于使用者讲,触发的一个事件Qt框架都会给我们发出某个特定的信号。
  • 信号的呈现方式其实就是函数,也就是说事件发生了,Qt框架会调用这个函数通知事件触发者。
  • Qt中信号的发出者是某个示例化的类对象。

槽的本质:

  • 槽就是对信号做出响应的函数。槽就是⼀个函数,与⼀般的 C++ 函数是⼀样的,可以定义在类的任何位置( public、protected 或 private ),可以具有任何参数,可以被重载,也可以被直接调用(但是不能有默认参数)。槽函数与⼀般的函数不同的是:槽函数可以与⼀个信号关联,当信号被触发时,关联的槽函数被自动执行

槽和信号底层其实本质上都是函数称之为槽函数和信号函数,他们之间的关联方式其实就是函数之间的互相调用关系。例如按下按钮的信号函数式clicked(),关闭窗口的槽函数是close()。实现点击按钮关闭窗口其实就是信号函数clicked()函数调用了槽函数close()

信号函数和槽函数通常位于某个类中,和普通的成员函数相比,它们的特别之处在于:

  • 信号函数用 signals 关键字修饰,槽函数用 public slots、protected slots 或者 private slots 修饰signals slots 是 Qt 在 C++ 的基础上扩展的关键字,专门用来指明信号函数和槽函数;
  • 信号函数只需要声明,不需要定义(实现),而槽函数需要定义(实现)。

3. 信号和槽的使用

3.1 connect函数的使用

在QObject类中提供了一个静态函数connect(),该函数专门用来关联指定的信号函数和槽函数。其中QObject是Qt内置的一个类,Qt中提供的很多内置类都是直接或者间接的继承自QObject,所以像QWidget,QPushButton等都可以直接使用connect函数。

connect的函数原型:

QObject::connect(const QObject *sender,  // 信号的发送者const char *signal, 	// 信号的类型const QObject *receiver, // 信号的接收者const char *method, 	// 信号的处理方式Qt::ConnectionType type = Qt::AutoConnection // ⽤于指定关联⽅式,默认的关联⽅式为 Qt::AutoConnection,通常不需要⼿动设定。)

所以我们就可以写一个demo代码来进行测试一下:

Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);// 这个 button的父窗口就是WidgetQPushButton* button = new QPushButton(this);button->setText("点击关闭函数");button->move(200, 200); // 这个就是设置坐标的函数,单位是像素点// 关联处理函数connect(button, &QPushButton::clicked, this, &QWidget::close);
}

3.2 查看内置信号和槽

在我们下载IDE的时候下载过了一个离线版本的文档。这个时候就需要去文档中寻找。

我们直接找到QPushButton类中找信号。但是因为Qt中存在许多的继承关系,所以我们搜素的类可能找不到对应的函数或者成员,这个时候就许需要到父类中查找。

在这里插入图片描述
所以这里我们就需要到QPushButton的父类中查找信号。

在这里插入图片描述
在这里插入图片描述

同理槽函数也是一样的道理。

3.2 connect的参数类型不匹配问题

这里我们重新回到connect函数的参数类型中我们会发现connect的第二个参数和第三个参数的的类型是const char*,但是我们实际传递的参数却是函数指针,这明显在C++中编译是会进行报错的,但是这里为什么没有报错呢?

在这里插入图片描述

在Qt5之前,我们是需要使用SIGNAL和SLOT宏来进行处理的,也就是写成:

connect(button, SINGAL(&QPushButton::clicked), this, SLOT(&QWidget::close));

但是在Qt5后进行了简化,并且还对connect函数进行了重载,我们可以去看看源码,这样就可以接收任意类型的了,而之所以文档写的是const char*,是因为没有对文档进行更新,所以一般我们看文档的时候都需要结合源码进行观看。

template <typename Func1, typename Func2>static inline QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal,const typename QtPrivate::FunctionPointer<Func2>::Object *receiver, Func2 slot,Qt::ConnectionType type = Qt::AutoConnection){

4. 自定义信号 && 自定义槽

4.1 自定义槽

  1. 使用纯代码方式进行自定义槽

自定义槽函数其实跟定义一个函数是一样的

Widget.h

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();
public slots: // 在Qt4之前必须写上这个,这里也建议写上,利于区分void setTitle();private:Ui::Widget *ui;
};

Widget.cpp

Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QPushButton* button = new QPushButton(this);button->setText("点击设置标题");button->move(200,200);connect(button, &QPushButton::clicked, this, &Widget::setTitle);
}Widget::~Widget()
{delete ui;
}void Widget::setTitle()
{this->setWindowTitle("设置标题成功");
}

在这里插入图片描述

  1. 使用图形化设计自定义槽——使用函数名的方式进行将信号和槽进行关联

在这里插入图片描述

选择槽函数类型
在这里插入图片描述
此时Qt会自动生成一个在Widget.h帮我们生成一个函数声明,在Widget.cpp生成一个函数定义,我们就可以在这个函数定义里面写我们的事件了。

Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);
}
Widget::~Widget()
{delete ui;
}
// 这个就是Qt自动帮我们生成的
void Widget::on_pushButton_clicked()
{this->setWindowTitle("设置标题成功");
}

而这里我们也发现发现了,Qt帮我们生成的这个信号和槽并没有使用到connect函数,就算我们在qmake根据widget.ui生成的C++代码ui_widget.h代码中也没有使用到connect函数进行讲信号和槽进行关联起来。

这是因为Qt可以通过函数名来将信号和槽进行关联起来,我们可以发现Qt给我们生成的槽函数的命令为on_pushButton_clicked,我们可以分成三部分on,pushButton,clicked,其中这三部分on是固定的,pushButton是我们使用图形化界面拖拽出来的按钮的名称,而clicked是我们选择的槽函数,所以合起来的意思就是将pushButton这个按钮和clicked进行关联,一旦触发信号就执行on_pushButton_clicked这个函数。

而其实这是因为Qt调用道理ui_widget.h中的的一个函数自动连接了信号槽的规则:

QMetaObject::connectSlotsByName(Widget);

4.2 自定义信号

自定义信号有以下几个规则:

  • 自定义信号函数必须写到"signals”下。
  • 返回值必须是void,可以有参数,可以进行重载。
  • 只需要实现声明,不需要实现定义。

发送信号:

  • 使用 “emit” 关键字发送信号 。“emit” 是⼀个空的宏。“emit” 其实是可选的,没有什么含义,只是为了提醒开发⼈员。

Widget.h

namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECT
public:Widget(QWidget *parent = nullptr);~Widget();
signals:void Mysignal(); // 只需要实现声明即可
private slots:void on_pushButton_clicked();void Myslots();
private:Ui::Widget *ui;
};

Widget.cpp

Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);connect(this, &Widget::Mysignal, this, &Widget::Myslots); // 将自定义信号和槽进行关联
}
Widget::~Widget()
{delete ui;
}
void Widget::on_pushButton_clicked()
{emit Mysignal(); // 发送信号
}
void Widget::Myslots()
{this->setWindowTitle("设置标题成功");
}

5. 带参数的信号和槽

信号和槽其实是可以带参数的,但是前提条件是槽函数的参数列表要和信号的参数列表一致,也就是说信号函数的参数类型是什么,槽函数的参数类型也必须是一样的,但是信号函数的参数列表可以比槽函数的参数列表多,前提是相同部分的位置的参数列表要相同。

Widget.h

class Widget : public QWidget
{Q_OBJECT public:Widget(QWidget *parent = nullptr);~Widget();
signals:void Mysignal(const QString&); // 只需要实现声明即可private slots:void on_pushButton_clicked();void Myslots(const QString&);private:Ui::Widget *ui;
};

Widget.cpp

Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);connect(this, &Widget::Mysignal, this, &Widget::Myslots);
}Widget::~Widget()
{delete ui;
}void Widget::on_pushButton_clicked()
{emit Mysignal("设置标题成功"); // 发送信号
}void Widget::Myslots(const QString& str)
{this->setWindowTitle(str);
}

这里也提醒一下,我们一个类要使用信号和槽的话必须包含Q_OBGECT这个宏,不然的话就会出错报错。

6. 信号与槽的关联方式

6.1 一对一

  1. 一个信号连接一个槽

我们上述举的例子都是一个信号连接一个槽。

  1. 一个信号连接另一个信号

Widget.h

class Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();
signals:void mysignal1();public slots:void myslots();private:Ui::Widget *ui;
};

Widget.cpp

Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QPushButton* button = new QPushButton(this);button->setText("点击按钮");connect(this, &Widget::mysignal1, &Widget::myslots);//信号关联信号connect(button, &QPushButton::clicked, this, &Widget::mysignal1);
}
Widget::~Widget()
{delete ui;
}
void Widget::myslots()
{this->setWindowTitle("设置标题成功");
}

6.2 一对多 && 多对一

  1. 一个信号关联多个槽
    widget.h
class Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();public slots:void myslots1();void myslots2();void myslots3();private:Ui::Widget *ui;
};

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include <QPushButton>
#include <QDebug>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QPushButton* button = new QPushButton(this);button->setText("点击按钮");// 一个信号关联多个槽connect(button, &QPushButton::clicked, this, &Widget::myslots1);connect(button, &QPushButton::clicked, this, &Widget::myslots2);connect(button, &QPushButton::clicked, this, &Widget::myslots3);
}Widget::~Widget()
{delete ui;
}void Widget::myslots1()
{qDebug() << "myslots1";
}
void Widget::myslots2()
{qDebug() << "myslots2";
}void Widget::myslots3()
{qDebug() << "myslots3";
}
  1. 多个信号关联一个槽

widget.h

class Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();signals:void mysignal1();void mysignal2();void mysignal3();public slots:void myslots1();void sentsignal();private:Ui::Widget *ui;
};

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include <QPushButton>
#include <QDebug>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QPushButton* button = new QPushButton(this);button->setText("点击按钮");connect(button, &QPushButton::clicked, this, &Widget::sentsignal);// 多个信号关联一个槽函数connect(this, &Widget::mysignal1, this, &Widget::myslots1);connect(this, &Widget::mysignal2, this, &Widget::myslots1);connect(this, &Widget::mysignal3, this, &Widget::myslots1);
}Widget::~Widget()
{delete ui;
}void Widget::myslots1()
{qDebug() << "myslots1";
}void Widget::sentsignal()
{emit mysignal1();emit mysignal1();emit mysignal1();
}

6.3 多对多

其实到这里都大差不差了,同样是复用上述代码

connect(this, &Widget::mysignal1, this, &Widget::myslots1);
connect(this, &Widget::mysignal2, this, &Widget::myslots2);
connect(this, &Widget::mysignal2, this, &Widget::myslots1);
connect(this, &Widget::mysignal3, this, &Widget::myslots1);

7. 信号与槽的断开

使用 disconnect 即可完成断开.
disconnect 的用法和 connect 基本⼀致.

disconnect(this, &Widget::mysignal1, this, &Widget::myslots1);

8. 使用Lambda表达式定义槽

Lambda表达式 是 C++11 增加的特性。C++11 中的 Lambda表达式 用于定义并创建匿名的函数对象,以简化编程⼯作。

[ capture ] ( params ) opt -> ret { Function body; 
};
  • capture 捕获列表
  • params 参数表
  • opt 函数选项
  • ret 返回值类型
  • Function body 函数体
符号说明
[ ]局部变量捕获列表。Lambda表达式不能访问外部函数体的任何局部变量
[a]在函数体内部使⽤值传递的⽅式访问a变量
[&b]在函数体内部使用引用传递的方式访问b变量
[=]函数外的所有局部变量都通过值传递的方式使用, 函数体内使用的是副本
[&]以引用的方式使用Lambda表达式外部的所有变量
[=, &foo]foo使用引用方式, 其余是值传递的方式
[&, foo]foo使用值传递方式,其余引用传递
[this]在函数内部可以使用类的成员函数和成员变量,= 和 & 形式也都会默认引入
  • 由于使用引用方式捕获对象会有局部变量释放了而Lambda函数还没有被调⽤的情况。如果执⾏Lambda函数,那么引⽤传递方式捕获进来的局部变量的值不可预知。所以绝大多数场合使用的形式为: [=] () { }
    •* 早期版本的 Qt,若要使用Lambda表达式,要在 “.pro” ⽂件中添加: CONFIG += C++11 因为 Lambda表式 是 C++11 标准提出的。Qt5 以上的版本无需手动添加,在新建项目时会自动添加。

在这里插入图片描述

#include "widget.h"
#include "ui_widget.h"
#include <QPushButton>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QPushButton* button = new QPushButton(this);button->move(200,200);button->setText("按钮");connect(button, &QPushButton::clicked, this, [=](){button->setWindowTitle("按钮被按下");button->move(300, 300);});
}Widget::~Widget()
{delete ui;
}

9. 信号和槽的优缺点

优点: 松散耦合

  • 信号发送者不需要知道发出的信号被哪个对象的槽函数接收,槽函数也不需要知道哪些信号关联了自己,Qt的信号槽机制保证了信号与槽函数的调用。支持信号槽机制的类或者父类必须继承于 QObject 类。

缺点: 效率较低

  • 与回调函数相比,信号和槽稍微慢⼀些,因为它们提供了更高的灵活性,尽管在实际应⽤程序中差别不大。通过信号调用的槽函数比直接调用的速度慢约10倍(这是定位信号的接收对象所需的开销;遍历所有关联;编组/解组传递的参数;多线程时,信号可能需要排队),这种调⽤速度对性能要求不是⾮常⾼的场景是可以忽略的,是可以满足绝大部分场景。

http://www.ppmy.cn/embedded/119310.html

相关文章

教师工作量|基于springBoot的教师工作量管理系统设计与实现(附项目源码+论文+数据库)

私信或留言即免费送开题报告和任务书&#xff08;可指定任意题目&#xff09; 目录 一、摘要 二、相关技术 三、系统设计 四、数据库设计 五、核心代码 六、论文参考 七、源码获取 一、摘要 传统信息的管理大部分依赖于管理人员的手工登记与管理&#xff…

记录linux环境下搭建本地MQTT服务器实现mqtt的ssl加密通讯

1、ubuntu安装mosquitto sudo apt-get update//安装服务端 sudo apt-get install mosquitto//安装客户端 sudo apt-get install mosquitto-clients 2、安装openssl 3、mqtts/tls加密传输 mosquitto原生支持了TLS加密&#xff0c;TLS&#xff08;传输层安全&#xff09;是SSL&…

基于python+spark的外卖餐饮数据分析系统设计与实现(含论文)-Spark毕业设计选题推荐

博主介绍&#xff1a; 大家好&#xff0c;本人精通Java、Python、C#、C、C编程语言&#xff0c;同时也熟练掌握微信小程序、Php和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我有丰富的成品Java、Python、C#毕设项目经验&#xff0c;能够为学生提供各类…

Word样式的同步与重置

有时候我们需要修改Word中的样式&#xff0c;实现排版的个性化。 如何同步样式到其他电脑上&#xff1f; Word中的样式是由Normal.dotm文件控制的&#xff0c;对样式所有的设置和修改&#xff0c;都会保存到这个问题件中&#xff0c;所以我们只需要在设置好样式以后&#xff…

JSP(Java Server Pages)基础使用二

简单练习在jsp页面上输出出乘法口诀表 既然大家都是来看这种代码的人了&#xff0c;那么这种输出乘法口诀表的这种简单算法肯定是难不住大家了&#xff0c;所以这次主要是来说jsp的使用格式问题。 <%--Created by IntelliJ IDEA.User: ***Date: 2024/7/18Time: 11:26To ch…

Kafka技术详解[1]:简介与基础概念

目录 1. Kafka入门 1.1 概述 1.1.1 初识Kafka 1.1.2 消息队列 1.1.3 生产者-消费者模式 1.1.4 消息中间件对比 1.1.5 ZooKeeper 1. Kafka入门 1.1 概述 1.1.1 初识Kafka Kafka是由Scala和Java语言开发的高吞吐量分布式消息发布和订阅系统&#xff0c;也是大数据技术领…

微软 Win11 24H2 RP 26100.1876 预览版发布!附详细更新日志

系统之家于9月24日发出最新报道&#xff0c;微软为Release Preview频道的Windows Insider项目成员&#xff0c;发布了适用Windows11 24H2版本更新的 KB5043178&#xff0c;更新后&#xff0c;系统版本号将升至26100.1876。此更新为用户带来了不同的新功能&#xff0c;例如打开开…

搜索:如何用 A*搜索算法实现游戏中的寻路功能?

搜索:如何用 A*搜索算法实现游戏中的寻路功能? 在游戏开发中,寻路功能是一个非常重要的部分。它可以让游戏中的角色自动找到从一个位置到另一个位置的最佳路径。A搜索算法是一种常用的寻路算法,它可以在复杂的地图环境中快速找到最短路径。本文将详细介绍如何用 A搜索算法…