当前位置: 首页 > news >正文

【QT八股文】系列之篇章2 | QT的信号与槽机制及通讯流程

【QT八股文】系列之篇章2 | QT的信号与槽机制及通讯流程

  • 前言
  • 2. 信号与槽
    • 信号与槽机制介绍/本质/原理,什么是Qt信号与槽机制?如何在Qt中使用?
    • 信号与槽机制原理,解析流程
    • Qt信号槽的调用流程
    • 信号与槽机制的优缺点
    • 信号与槽机制需要注意的问题
    • 信号的注意点
    • 信号与槽与回调函数区别
    • Qt信号与槽的多种用法
    • PYQT5 connect 函数
    • Qt connect 函数的连接方式
    • PYQT5信号槽的链接方式
    • 信号槽同步与异步/多线程下,信号槽分别在什么线程中执行,如何控制——`Qt connect 函数的连接方式`来控制
  • 3. 通讯流程
    • QT的TCP通讯流程
    • QT的UDP通讯流程
  • 下一章笔记
  • 说明

前言

第一篇章主要是基础定义及QT中重要的事件机制
笔记链接:【QT八股文】系列之篇章1 | QT的基础知识及事件/机制

这里我们在了解了QT的大概后,我们将来了解QT中的核心机制:信号与槽
因为介绍到信号与槽,所以笔者我会讲通讯流程提前在前面来介绍

原创文章,未经同意请勿转载

2. 信号与槽

信号与槽机制介绍/本质/原理,什么是Qt信号与槽机制?如何在Qt中使用?

在这里插入图片描述

  • 定义
    Qt信号与槽机制是一种基于事件机制的编程模型,用于对象之间的通信。信号是由发送方对象发射的事件,而槽是接收方对象用于处理这些事件的函数。在Qt中,我们可以使用QObject类中的信号和槽机制来实现对象间的通信。通过定义信号和槽函数,在信号发射时,会自动调用对应的槽函数进行处理。

  • 使用
    PyQt的内置信号是自动定义的,使用PyQt5.QtCore.pyqtSignal函数可以为QObject对象创建一个信号,使用pyqtSignal函数可以把信号定义为类的属性。使用connect函数可以将信号绑定到槽函数上,使用disconnect函数可以解除信号与槽函数的绑定,使用emit函数可以发射信号。

  • 本质(就是回调函数)
    在事件的处理方面,信号槽相比回调函数,具有类型安全、松耦合、任意参数的优势,但执行效率会有一点损失。信号相当于传递参数(指实参,用于传递值/动作变化),槽函数像用于传递函数体(形参/函数体,用于接收值/根据动作变化来做出对应操作。)

  • 原理

    1. Qt 中的信号与槽机制是一种事件处理机制,它允许程序在接收到特定事件时执行特定的操作。在 Qt 中,信号与槽机制被广泛应用于组件之间的通信和事件处理。
    2. 具体来说,Qt 中的信号与槽机制是基于 QObject 类的。任何一个 QObject 对象都可以作为一个信号源,它可以通过 emit() 方法发出信号。同时,任何一个 QObject 对象都可以作为一个槽,它可以接受并处理来自信号源的信号。当一个信号源发出信号时,它会连接到相应的槽。这些槽可以是与信号源同一个对象,也可以是其他 QObject 对象。当信号源接收到信号时,它会将信号传递给所有已经连接到该槽的对象。这些对象会在接收到信号时执行相应的操作。

信号与槽机制原理,解析流程

在这里插入图片描述

  • 原理

    1. Qt 中的信号与槽机制是一种事件处理机制,它允许程序在接收到特定事件时执行特定的操作。在 Qt 中,信号与槽机制被广泛应用于组件之间的通信和事件处理。
    2. 具体来说,Qt 中的信号与槽机制是基于 QObject 类的。任何一个 QObject 对象都可以作为一个信号源,它可以通过 emit() 方法发出信号。同时,任何一个 QObject 对象都可以作为一个槽,它可以接受并处理来自信号源的信号。当一个信号源发出信号时,它会连接到相应的槽。这些槽可以是与信号源同一个对象,也可以是其他 QObject 对象。当信号源接收到信号时,它会将信号传递给所有已经连接到该槽的对象。这些对象会在接收到信号时执行相应的操作。
  • 解析流程

    1. moc查找头文件中的signals,slots,标记出信号和槽。
    2. 将信号槽信息存储到类静态变量staticMetaObject中,并且按声明顺序进行存放,建立索引。
    3. 当发现有connect连接时,将信号槽的索引信息放到一个map中,彼此配对。
    4. 当调用emit时,调用信号函数,并且传递发送信号的对象指针,元对象指针,信号索引,参数列表到active函数
    5. 通过active函数找到在map中找到所有与信号对应的槽索引
    6. 根据槽索引找到槽函数,执行槽函数。

Qt信号槽的调用流程

注意:信号槽的实现:元对象编译器MOC,MOC的本质就是反射器

  • MOC(元对象编译器)查找头文件中的signal与slots,标记出信号槽。将信号槽信息储存到类静态变量staticMetaObject中,并按照声明的顺序进行存放,建立索引。
  • connect链接,将信号槽的索引信息放到一个双向链表中,彼此配对。
  • emit被调用,调用信号函数,且传递发送信号的对象指针,元对象指针,信号索引,参数列表到active函数。
  • active函数在双向链表中找到所有与信号对应的槽索引,根据槽索引找到槽函数,执行槽函数。

信号与槽机制的优缺点

  • 优点
    • 类型安全。需要关联的信号槽的签名必须是等同的。即信号的参数类型和参数个数同接受该信号的槽的参数类型和参数个数相同。若信号和槽签名不一致,编译器会报错。信号的参数可以多于槽,槽参数数量不能大于于信号

      💡 槽函数的参数是否可以比信号的参数多?
      也可以。唯一的情况就是槽函数参数带有默认参数,除去默认参数外,槽函数的参数必须小于等于信号的参数。

    • 松散耦合。QT的信号槽的建立和解除绑定十分自由。信号和槽机制减弱了Qt对象的耦合度。激发信号的Qt对象无需知道是那个对象的那个信号槽接收它发出的信号,它只需在适当的时间发送适当的信号即可,而不需要关心是否被接受和那个对象接受了。Qt就保证了适当的槽得到了调用,即使关联的对象在运行时被删除。程序也不会奔溃。

      💡 信号重载了,如何确定连接哪个信号?
      采用函数指针确定连接哪个信号。

    • 灵活性。一个信号可以关联多个槽,或多个信号关联同一个槽。

  • 不足
    • 速度较慢。与回调函数相比,信号和槽机制运行速度比直接调用非虚函数慢10倍。信号槽同真正的回调函数比起来时间的耗损还是很大的,所以在嵌入式实时系统中应当慎用。
      • 原因:
        • ①需要定位接收信号的对象。
        • ②安全地遍历所有关联槽。
        • ③编组、解组传递参数。
        • ④多线程的时候,信号需要排队等待。(然而,与创建对象的new操作及删除对象的delete操作相比,信号和槽的运行代价只是他们很少的一部分。信号和槽机制导致的这点性能损耗,对实时应用程序是可以忽略的。
    • 不能出现宏定义。信号槽的参数限定很多例如不能携带模板类参数,不能出现宏定义等等。

信号与槽机制需要注意的问题

信号与槽机制是比较灵活的,但有些局限性我们必须了解,这样在实际的使用过程中才能够做到有的放矢,避免产生一些错误。下面就介绍一下这方面的情况。

  • 信号与槽的效率是非常高的,但是同真正的回调函数比较起来,由于增加了灵活 性,因此在速度上还是有所损失,当然这种损失相对来说是比较小的,通过在一台 i586- 133 的机器上测试是 10 微秒(运行 Linux),可见这种机制所提供的简洁性、灵活性还是 值得的。但如果我们要追求高效率的话,比如在实时系统中就要尽可能的少用这种机制。
  • 信号与槽机制与普通函数的调用一样,如果使用不当的话,在程序执行时也有可能 产生死循环。因此,在定义槽函数时一定要注意避免间接形成无限循环,即在槽中再次发射 所接收到的同样信号。
  • 如果一个信号与多个槽相关联的话,那么,当这个信号被发射时,与之相关的槽被 激活的顺序将是随机的,并且我们不能指定该顺序。
  • 宏定义不能用在 signal 和 slot 的参数中。
  • 构造函数不能用在 signals 或者 slots 声明区域内。
  • 函数指针不能作为信号或槽的参数。
  • 信号与槽不能有缺省参数。
  • 信号与槽也不能携带模板类参数。

信号的注意点

  • 所有的信号声明都是公有的,所以Qt规定不能在signals前面加public,private, protected。
  • 所有的信号都没有返回值,所以返回值都用void。
  • 所有的信号都不需要定义。
  • 必须直接或间接继承自QOBject类,并且开头私有声明包含Q_OBJECT。
  • 在同一个线程中,当一个信号被emit发出时,会立即执行其槽函数,等槽函数执行完毕后,才会执行emit后面的代码,如果一个信号链接了多个槽,那么会等所有的槽函数执行完毕后才执行后面的代码,槽函数的执行顺序是按照它们链接时的顺序执行的。不同线程中(即跨线程时),槽函数的执行顺序是随机的。
  • 在链接信号和槽时,可以设置链接方式为:在发出信号后,不需要等待槽函数执行完,而是直接执行后面的代码,是通过connect的第5个参数。
  • 信号与槽机制要求信号和槽的参数一致,所谓一致,是参数类型一致。如果不一致,允许的情况是,信号的参数可以比槽函数的参数多,即便如此,槽函数存在的那些参数的顺序也必须和信号的前面几个一致起来。这是因为,你可以在槽函数中选择忽略信号传来的数据(也就是槽函数的参数比信号的少),但是不能说信号根本没有这个数据,你就要在槽函数中使用(就是槽函数的参数比信号的多,这是不允许的)。

信号与槽与回调函数区别

  1. 链接的不同

    • 回调函数使用函数指针来实现的,如果多个类都关注一个类的动态变化,这样就会需要写出一个比较长的列表来管理这些类之间的关系。稍微在编码方面不那么灵活,稍显冗余。
    • QT使用信号与槽来解决这个连接问题,这种方式比较清晰简单一些,一个类只需要清楚自己有几个槽函数有几个信号,然后将信号与槽进行连接,QT会自己处理函数的调用关系。这样在软件设计角度更加的清晰,灵活,不容易出错。
  2. 执行顺序/时间的不同

    • Qt 信号与槽机制中的槽函数在接收到信号时会自动执行,而回调函数通常是在调用时立即执行。Qt 信号与槽机制可以在信号触发时立即执行槽函数,也可以延迟执行槽函数,而回调函数通常是立即执行的。
    • 信号与槽机制中的信号与槽之间的执行顺序是不确定的,可以是任意顺序,也可以是逆序;而回调函数机制中的回调函数之间的执行顺序通常是确定的,按照函数声明的顺序执行。
  3. 对象绑定

    信号与槽机制可以实现对象之间的动态绑定,可以在运行时动态地绑定信号与槽;而回调函数机制通常只能在程序启动时进行绑定。

  4. 主要用途不同

    信号和槽机制是用于在程序运行时传递数据和事件的机制,而回调函数则通常被用于函数或方法的调用。因此,信号和槽机制可以用于模块之间的通信和交互,而回调函数则通常用于函数或方法的调用。

Qt信号与槽的多种用法

  • 一个信号可以和多个槽相连
    这时槽的执行顺序和在不在同一个线程上有关,同一线程,槽的执行顺序和声明顺序有关,跨线程时,执行顺序是不确定的。
  • 多个信号可以连接到一个槽
    只要任意一个信号发出,这个槽就会被调用。
  • 一个信号可以连接到另外的一个信号
    当第一个信号发出时,第二个信号被发出。除此之外,这种信号-信号的形式和信号-槽的形式没有什么区别。
  • 槽可以被取消链接
    这种情况并不经常出现,因为当一个对象delete之后,Qt自动取消所有连接到这个对象上面的槽。想主动取消连接就用disconnect()函数中添加任何实现。
  • 可以使用Lambda 表达式
    在使用 Qt 5 的时候,能够支持 Qt 5 的编译器都是支持 Lambda 表达式的。

PYQT5 connect 函数

注:在Qt中第五个参数用于指定信号与槽的匹配规则。而PYQT5是第四个参数
在 PyQt5 中,connect 函数【connect: PyQt5.QtWidgets.QSignalMapper()】是一个用于连接信号与槽的函数。它通常被用于将对象的信号与槽函数进行连接。

列子:mapper = Qt.QSignalMapper() mapper.setMapping(button, button.clicked.connect(mapper.setCurrentIndex))

第一个参数是一个可选的参数,用于指定要连接的信号源。如果该参数为 None,则表示连接的是系统提供的信号。如果该参数不为 None,则表示要连接自定义信号。

第二个参数是一个可选的参数,用于指定要连接的槽函数。如果该参数为 None,则表示连接的是默认槽函数。如果该参数不为 None,则表示要连接指定的槽函数。

第三个参数是一个字符串,用于指定信号与槽之间的映射关系。该字符串通常由信号名称和槽函数名称组成。例如,“clicked” 表示将按钮的点击信号与按钮的 clicked 槽函数进行连接。

第四个参数是一个 PyQt5 中的 QSignalMapper 对象,用于指定信号与槽的匹配规则。该对象应该实现 QSignalMapper 类中的方法,例如 setMapping() 和 currentIndex() 等。

第五个参数是一个可选的参数,用于指定信号中断连接的函数。如果连接的信号源对象被删除或重新分配,则连接将被中断。默认情况下,连接不会自动中断。

Qt connect 函数的连接方式

  1. 自动连接Qt::AutoConnection
    默认值,使用这个值则连接类型会在信号发送时决定。如果接收者和发送者在同一个线程,则自动使用
    多线程时为队列连接函数,单线程时为直接连接函数。

  2. 直接连接Qt::DirectConnection
    == 如果接收者和发送者不在一个线程,则自动使用Qt::QueuedConnection类型。==
    Qt::DirectConnection:槽函数会在信号发送的时候直接被调用,槽函数和信号发送者在同一线程。效果看上去就像是直接在信号发送位置调用了槽函数,效果上看起来像函数调用,同步执行。
    emit语句后面的代码将在与信号关联的所有槽函数执行完毕后才被执行。
    信号/槽在信号发出者所在的线程中执行

  3. 队列连接Qt::QueuedConnection
    信号发出后,信号会暂时被放到一个消息队列中,需等到接收对象所属线程的事件循环取得控制权时才取得该信号,然后执行和信号关联的槽函数,这种方式既可以在同一线程内传递消息也可以跨线程操作。
    emit语句后的代码将在发出信号后立即被执行,无需等待槽函数执行完毕
    信号在信号发出者所在的线程中执行,槽函数在信号接收者所在的线程中执行

  4. Qt::BlockingQueuedConnection
    槽函数的调用时机与Qt::QueuedConnection一致,不过发送完信号后发送者所在线程会阻塞,直到槽函数运行完。而且接收者和发送者绝对不能在一个线程,否则程序会死锁。在多线程间需要同步的场合可能需要这个。

  5. Qt::UniqueConnection:这个flag可以通过按位或(|)与以上四个结合在一起使用。当这个flag设置时,当某个信号和槽已经连接时,再进行重复的连接就会失败。也就是为了避免重复连接。

PYQT5信号槽的链接方式

在 PyQt5 中,信号与槽的连接方式有两种:1. 使用 connect() 函数;2. 装饰器@pyqtSlot() 。

@pyqtSlot()优点是方式书写比较简洁。缺点是但函数名称不能自由定义,在想自定义参数时没有详细说明。

connect()方式优点是理解和学习起来比较简单,而且函数名称可以自由定义。缺点是但如果信号比较多时,书写就比较混乱。

使用信号处理器的优点是可以在信号发生时执行复杂的操作,而缺点是连接信号处理器需要花费更多的内存和时间,并且连接信号处理器需要手动管理连接关系。因此,使用信号处理器仅适用于需要执行复杂操作的情况。

  1. 装饰器方法:@pyqtSlot()装饰器

    @pyqtSlot():修饰关键词,表明下面是完整的信号槽函数

    # 需要引入 pyqtSlot 库函数
    from PyQt5.QtCore import pyqtSlot@pyqtSlot() #装饰器,此函数没有connect直接通过装饰器初始化连接槽函数
    def on_pushButton_clicked(self)print("我点击了")
    

    在@pyqtSlot()方式里,函数名称有特殊要求,如下:

    def  on_(控件对象名)_信号名(self,内置参数)

    @pyqtSlot()控制控件的多信号

    @pyqtSlot()
    def on_lineEdit_returnPressed(self):print('触发了信号 returnPressed')def on_lineEdit_textChanged(self):print('触发了信号 textChanged')
    

    注意:一个控件同时要写多个信号与槽函数时,只需要写一遍@pyqtSlot()关键词,中间可以有其他函数隔开。一定是一个类里面的,一个控件只写一遍@pyqtSlot(),不是所有控件信号只写一次@pyqtSlot(),有多少控件的信号还是要写。

  2. connect连接法

    使用 connect() 函数将信号与槽函数连接起来。connect() 函数接受两个参数:要连接的信号和要连接的槽函数。连接成功后,当信号发生时,槽函数将被调用。

    # 在初始化函数中信号连接槽函数
    self.pushButton.clicked.connect(self.test)
    # 槽函数
    def test(self):print("点击了一下")
    

    规则:

    • 语法规则:self.控件对象名称.信号名称.connect(self.槽函数名称)
    • 有参数时,槽函数名称部分写成lambda 参数名: 函数名(参数名)
    • 没有参数时,槽函数不用写括号()

信号槽同步与异步/多线程下,信号槽分别在什么线程中执行,如何控制——Qt connect 函数的连接方式来控制

可以通过QT的connect函数的第五个参数(PYQT5中是第四个参数)来控制, 信号槽执行时所在的线程。

通常使用的connect,实际上最后一个参数使用的是Qt::AutoConnection类型:Qt支持6种连接方式,其中3中最主要:

  1. Qt::AutoConnection(自动方式)

    信号槽在信号发出者所在的线程中执行

    Qt的默认连接方式,如果信号的发出和接收这个信号的对象同属一个线程,那个工作方式与直连方式相同(会自动使用Qt::DirectConnection类型);否则工作方式与排队方式相同(会自动使用Qt::QueuedConnection类型)。

    即多线程时为队列连接函数,单线程时为直接连接函数。

  2. Qt::DirectConnection(直连方式)(信号与槽函数关系类似于函数调用,同步执行)

    当信号发出后,相应的槽函数将立即被调用。emit语句后的代码将在所有槽函数执行完毕后被执行。

  3. Qt::QueuedConnection(排队方式)(此时信号被塞到信号队列里了,信号与槽函数关系类似于消息通信,异步执行)

    信号在信号发出者所在的线程中执行,槽函数在信号接收者所在的线程中执行

    当信号发出后,排队到信号队列中,需等到接收对象所属线程的事件循环取得控制权时才取得该信号,调用相应的槽函数。emit语句后的代码将在发出信号后立即被执行,无需等待槽函数执行完毕。

  4. Qt::BlockingQueuedConnection(信号和槽必须在不同的线程中,否则就产生死锁)

    这个是完全同步队列只有槽线程执行完成才会返回,否则发送线程也会一直等待,相当于是不同的线程可以同步起来执行

    与Qt::QueuedConnection相同,除了信号线程阻塞直到槽返回。如果接收方处于发送信号的线程中,则不能使用此连接,否则应用程序将死锁。

  5. Qt::UniqueConnection

    与默认工作方式相同,只是不能重复连接相同的信号和槽,因为如果重复连接就会导致一个信号发出,对应槽函数就会执行多次。

    这个flag可以通过按位或(|)与以上四个结合在一起使用。当这个flag设置时,当某个信号和槽已经连接时,再进行重复的连接就会失败。也就是为了避免重复连接。

  6. Qt::AutoCompatConnection

    是为了连接Qt4与Qt3的信号槽机制兼容方式,工作方式与Qt::AutoConnection一样。

3. 通讯流程

QT的TCP通讯流程

在这里插入图片描述
QT如果要进行网络编程首先需要在.pro中添加如下代码:QT += network

  • 服务端:(QTcpServer)
    ① 创建QTcpServer对象
    ② 监听list需要的参数是地址和端口号
    ③ 当有新的客户端连接成功回发送newConnect信号
    ④ 在newConnection信号槽函数中,调用nextPendingConnection函数获取新连接QTcpSocket对象
    ⑤ 连接QTcpSocket对象的readRead信号
    ⑥ 在readRead信号的槽函数使用read接收数据
    ⑦ 调用write成员函数发送数据

  • 服务器端

    1. 创建用于监听的套接字

    2. 给套接字设置监听

    3. 如果有连接到来, 监听的套接字会发出信号newConnected

    4. 接收连接, 通过nextPendingConnection()函数, 返回一个QTcpSocket类型的套接字对象(用于通信)

    5. 使用用于通信的套接字对象通信 1>. 发送数据: write 2>. 接收数据: readAll/read

      • 代码

        Widget::Widget(QWidget *parent) :QWidget(parent),ui(new Ui::Widget)
        {ui->setupUi(this);tcpServer = new QTcpServer;tcpServer->listen(QHostAddress("192.168.0.111"),1234);connect(tcpServer,SIGNAL(newConnection()),this,SLOT(new_connect()));
        }Widget::~Widget()
        {delete ui;
        }void Widget::new_connect()
        {qDebug("--new connect--");QTcpSocket* tcpSocket = tcpServer->nextPendingConnection();connect(tcpSocket,SIGNAL(readyRead()),this,SLOT(read_data()));socketArr.push_back(tcpSocket);}void Widget::read_data()
        {for(int i=0; i<socketArr.size(); i++){if(socketArr[i]->bytesAvailable()){char buf[256] = {};socketArr[i]->read(buf,sizeof(buf));qDebug("---read:%s---",buf);}}
        }
        
  • 客户端:(QTcpSocket)
    ① 创建QTcpSocket对象
    ② 当对象与Server连接成功时会发送connected 信号
    ③ 调用成员函数connectToHost连接服务器,需要的参数是地址和端口号
    ④ connected信号的槽函数开启发送数据
    ⑤ 使用write发送数据,read接收数据

  • 客户端:

    1. 创建用于通信的套接字
    2. 连接服务器: connectToHost
    3. 连接成功与服务器通信
      1 >. 发送数据: write 2>. 接收数据: readAll/read
    • 代码

      Widget::Widget(QWidget *parent) :QWidget(parent),ui(new Ui::Widget)
      {ui->setupUi(this);tcpSocket = new QTcpSocket;connect(tcpSocket,SIGNAL(connected()),this,SLOT(connect_success()));tcpSocket->connectToHost("172.20.10.3",1234);
      }Widget::~Widget()
      {delete ui;
      }void Widget::on_send_clicked()
      {std::string msg = ui->msg->text().toStdString();int ret = tcpSocket->write(msg.c_str(),msg.size()+1);qDebug("--send:%d--",ret);
      }void Widget::connect_success()
      {ui->send->setEnabled(true);
      }
      

QT的UDP通讯流程

UDP(User Datagram Protocol即用户数据报协议)是一个轻量级的,不可靠的,面向数据报的无连接协议。在网络质量令人十分不满意的环境下,UDP协议数据包丢失严重。由于UDP的特性:它不属于连接型协议,因而具有资源消耗小,处理速度快的优点,所以通常音频、视频和普通数据在传送时使用UDP较多,因为它们即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。所以QQ这种对保密要求并不太高的聊天程序就是使用的UDP协议。

在Qt中提供了QUdpSocket 类来进行UDP数据报(datagrams)的发送和接收。Socket简单地说,就是一个IP地址加一个port端口 。

QT下UDP通信服务器端和客户端的关系是对等的, 做的处理也是一样的:

  1. 创建套接字对象 2. 如果需要接收数据, 必须绑定端口 3. 发送数据: writeDatagram 4. 接收数据: readDatagram

流程:①创建QUdpSocket套接字对象 ②如果需要接收数据,必须绑定端口 ③发送数据用writeDatagram,接收数据用 readDatagram 。

下一章笔记

下篇笔记链接:【QT的多线程以及QThread与QObject】
下篇笔记主要内容:QT的多线程以及QThread与QObject

说明

码字不易,可能当中存在某些字体错误(笔者我没有发现),如果有错误,欢迎大家指正。🤗
另外因为笔记是之前做的,这里我只把我之前做的搬移和重新排版过来,如果有知识上的错误也欢迎大家指正。

相关文章:

【QT八股文】系列之篇章2 | QT的信号与槽机制及通讯流程

【QT八股文】系列之篇章2 | QT的信号与槽机制及通讯流程 前言2. 信号与槽信号与槽机制介绍/本质/原理&#xff0c;什么是Qt信号与槽机制&#xff1f;如何在Qt中使用&#xff1f;信号与槽机制原理&#xff0c;解析流程Qt信号槽的调用流程信号与槽机制的优缺点信号与槽机制需要注…...

excel表格里怎样不删除0,又不显示0呢?

在单元格里不显示0&#xff0c;大体上有这么几种方法&#xff1a; 1.设置单元格自定义格式 选中数据区域&#xff0c;鼠标右键&#xff0c;点一下设置单元格格式&#xff0c;选中数字&#xff0c;自定义&#xff0c;在右侧的类型栏&#xff0c;设置格式&#xff1a; [0]&quo…...

精准操控时间的艺术:JavaScript节流函数的深度探索与实践【含代码示例】

精准操控时间的艺术&#xff1a;JavaScript节流函数的深度探索与实践【含代码示例】 节流基础&#xff1a;概念与作用实现策略&#xff1a;案例展示案例一&#xff1a;基础定时器实现案例二&#xff1a;立即执行版本案例三&#xff1a;使用requestAnimationFrame实现动画节流 功…...

自学SPSS,有哪些教学视频或书籍推荐?

书籍推荐 经过长达八年的不断迭代与优化&#xff0c;SPSSAU的用户群体已经远超简单的数据分析层面&#xff0c;而是逐步深入到了学术研究的精髓之中。如今&#xff0c;无论是在SCI、EI等国际权威学术期刊&#xff0c;还是北大核心期刊、CSSCI等国内顶尖学术期刊上&#xff0c;…...

开源数据库同步工具DBSyncer

前言&#xff1a; 这么实用的工具&#xff0c;竟然今天才发现&#xff0c;相见恨晚呀&#xff01;&#xff01;&#xff01;&#xff01; DBSyncer&#xff08;英[dbsɪŋkɜː]&#xff0c;美[dbsɪŋkɜː 简称dbs&#xff09;是一款开源的数据同步中间件&#xff0c;提供M…...

【SpringMVC】_SpringMVC项目返回HTML与JSON

目录 1. SpringMVC项目返回HTML页面 2. SpringMVC项目返回JSON 2.1 程序演示 2.2 关于响应的Content-Type 2.2.1 接口为对象 2.2.2 接口为String 2.2.3 接口为Map 本专栏已介绍&#xff1a; 返回静态页面&#xff1a; 【Spring MVC】_SpringMVC项目返回静态页面_mvc 返…...

STL库--stack

目录 stack的定义 stack容器内元素的访问 stack常用函数实例解析 stack的常见用途 stack的定义 其定义的写法和其他STL容器相同&#xff0c;typename可以任意基本类型或容器&#xff1a; stack<typename> name; stack容器内元素的访问 由于栈本身就是一种后进先出…...

从System Prompt来看Claude3、Kimi和ChatGLM4之间的差距

大家好,我是herosunly。985院校硕士毕业,现担任算法t研究员一职,热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的见解。曾经辅导过若干个非计算机专业的学生进入到算…...

(精确度,召回率,真阳性,假阳性)ACC、敏感性、特异性等 ROC指标

&#xff08;精确度&#xff0c;召回率&#xff0c;真阳性&#xff0c;假阳性&#xff09;ACC、敏感性、特异性等 ROC指标...

Docker安装nginx详细教程

详细教程如下&#xff1a; 1. 拉取Nginx镜像 docker pull nginx默认拉最新的&#xff08;也可以根据自己的需求指定版本&#xff09; 2. 运行Nginx容器 docker run --name my-nginx -d -p 80:80 nginx--name my-nginx&#xff1a;容器名称&#xff0c;便于管理。-d&#xf…...

FineBI学习总结

大数据分析BI工具&#xff1a;用户只需简单拖拽便能制作出丰富多样的数据可视化信息 关注点&#xff1a; 快速入门、数据加工、构建图表和分析数据、数据分析进阶 1、界面介绍 目录–仪表板–数据准备 仪表板目录–预览区域 快速上手&#xff1a; 1、数据准备2、制作仪表板3、分…...

现代操作系统上创建各类链接的方法汇总

文章目录 现代操作系统上创建各类链接的方法汇总windows: cmd下的mklink创建链接示例 powershell 创建链接创建常规文件和目录创建链接 linux shell 创建硬链接NAMESYNOPSIS详细说明常用选项示例 检查与辨识符号链接&#x1f388;linux下检查ls -l 命令file 命令 windows下检查…...

CSS中的Flex布局

目录 一.什么是Flex布局 二.Flex布局使用 2.1Flex使用语法 2.2基本概念 三.容器的属性 3.1所有属性概述 3.2flex-direction 3.3flex-wrap 3.4flex-flow 3.5justify-content 3.6align-items 3.7align-content 四.项目(子元素)的属性 4.1所有属性概述 4.2order 4…...

基于扩散模型的,开源世界模型DIAMOND

日内瓦大学、微软研究院和爱丁堡大学的研究人员联合开源了&#xff0c;基于扩散模型的世界模型—DIAMOND。 研究人员之所以选择扩散模型作为基础&#xff0c;是因为可以更好地捕捉视觉细节&#xff0c;同时具有建模复杂多模态分布的能力&#xff0c;以便在不同的环境下进行训练…...

【MySQL精通之路】InnoDB存储引擎

欢迎大家关注Innodb存储引擎专栏&#xff1a; http://t.csdnimg.cn/gEsSn 以下是innodb存储引擎相关所有的的知识点。 1.InnoDB简介 2.InnoDB与ACID模型 3.InnoDB多版本 4.InnoDB体系结构 下图显示了构成InnoDB存储引擎体系结构的内存和磁盘结构。 有关每个结构的信息&…...

【创作活动】探索 GPT-4o:下一代语言模型的技术革命

&#x1f604; 19年之后由于某些原因断更了三年&#xff0c;23年重新扬帆起航&#xff0c;推出更多优质博文&#xff0c;希望大家多多支持&#xff5e; &#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Mi…...

【热门话题】Debian常用命令指南

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 Debian常用命令指南引言1. 文件与目录操作lscdmkdirrmcpmv 2. 包管理aptdpkg 3.…...

人大金仓 KingBase查询死锁,释放死锁

人大金仓(kingbase)查询数据库死锁及释放 kingbase锁表排查以及释放锁 总结下 -- 查询&#xff0c;可自己添加where条件 SELECT * FROM sys_stat_activity WHERE state ! idle AND wait_event_typeLock-- 结束进程 SELECT sys_terminate_backend(pid);...

C++高手进阶:Windows 模块加载的艺术与策略

前文我们讲到了怎么不依赖第三库&#xff0c;搭建自己的测试框架 没有看的读者可以通过这个链接自行阅读&#xff1a; &#x1f449;&#x1f449;&#x1f449; 自力更生&#xff1a;0依赖三方库&#xff0c;手把手教你打造专属C测试框架 作为项目开发来说&#xff0c;我们通常…...

基于STM32单片机老人体温心率血氧跌倒定位短信报警

一.硬件及设计功能 以STM32F103C8T6为中央处理器&#xff0c;GPS模块用采集数据&#xff0c;将数据发送给单片机后&#xff0c;单片机根据定位计算公式得出当前位置的经纬度信息和时间信息。经过LCD显示器处理后得出和时间信息SIM800模块发送短信到设定的手机号上&#xff0c;将…...

【测评】雨云香港三区云服务器,2核2G 5兆,仅需38元/月

写在前面 雨云香港三区云服务器&#xff0c;高性能的 AMD EPYC 处理器 企业级 NVME SSD 高性能云服务器。2核2G 10兆 400G防御&#xff0c;仅需38元/月&#xff0c;年付7折仅 319.2元/年。 官网&#xff1a;https://www.rainyun.com 本次测评服务器配置如下&#xff1a; C…...

如何应对Android面试官 -> 玩转 Fragment

前言 本章主要讲解下 Framgent 的核心原理&#xff1b; 基础用法 线上基础用法&#xff0c;其他的可以自行百度 FragmentManager manager getSupportFragmentManager(); FragmentTransaction transaction manager.beginTransaction(); transaction.add(R.id.contentlayout,…...

sdbusplus:通过文件描述符传递数据

有的时候需要传递大量的数据,如果将数据通过dbus传递,会消耗大量的带宽。可以通过传递一个文件描述符替代传递数据: 以下的service通过文件描述符接收数据: //fd_service.cpp #include <sdbusplus/asio/connection.hpp> #include <sdbusplus/asio/object_server…...

HyperLPR3 车牌识别

Linux 之前安装了python3 apt install python3.8-venv cd /root python3 -m venv HyperLPR3 REM cd HyperLPR3 source HyperLPR3/bin/activate 参考 https://www.jb51.net/article/222885.htm python -m pip install hyperlpr3 里面有fastapi&#xff0c;opencv等 错误&#x…...

面试的内容

1.C的三大特性&#xff1a;封装&#xff0c;继承&#xff0c;多态 2.C11的特性 3.NULL与Nullptr的区别: nullptr是一个特殊的空指针常量&#xff0c;不能被隐式转换为其他类型。 NULL 在一些情况下可能会发生隐式类型转换 4.智能指针 5.stl/Qt stl stl容器包含哪些&…...

剪映网页版

https://www.capcut.cn/web 免费&#xff0c;免安装&#xff0c;跨平台&#xff0c;视频云合成&#xff0c;简直太好用了&#xff01;...

pgsql

创建分区表&#xff1a; PostgreSQL分区表_pg分区表-CSDN博客 创建list分区的函数 create or replace function create_list_fq(tb_name char, row_name char) returns int AS $$ declares char; beginraise notice CREATE TABLE if not exists %_% PARTITION OF % FOR VALU…...

Kotlin学习笔记 泛型

在 Kotlin 中&#xff0c;T 通常用作类型参数的占位符&#xff0c;它在实例化或传递参数时会被替换成具体的类型。 Kotlin 支持泛型&#xff0c;这意味着您可以编写可以与多种数据类型一起工作的代码&#xff0c;而不必为每种数据类型编写单独的代码。 ### 泛型类和函数 在 …...

开发者必看:Linux终端的10大装逼神器,让你的命令行炫酷起来!

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…...

20 VUE学习:插件

介绍 插件 (Plugins) 是一种能为 Vue 添加全局功能的工具代码。下面是如何安装一个插件的示例&#xff1a; import { createApp } from vueconst app createApp({})app.use(myPlugin, {/* 可选的选项 */ })一个插件可以是一个拥有 install() 方法的对象&#xff0c;也可以直接…...