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

news2024/10/7 9:21:14

【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

说明

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1690362.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【软考中级 软件设计师】数据结构

数据结构是计算机科学中一个基础且重要的概念&#xff0c;它研究数据的存储结构以及在此结构上执行的各种操作。在准备软考中级-软件设计师考试时&#xff0c;掌握好数据结构部分对于通过考试至关重要。下面是一些核心知识点概览&#xff1a; 基本概念&#xff1a; 数据结构定义…

在NVIDIA Jetson Nano上部署YOLOv5算法,并使用TensorRT和DeepStream进行加速

部署YOLOv5算法在NVIDIA Jetson Nano上并使用TensorRT和DeepStream进行加速涉及几个关键步骤。下面是一个详细的指南&#xff1a; 步骤 1: 准备YOLOv5模型 训练或下载预训练模型&#xff1a;首先&#xff0c;你需要有一个YOLOv5模型。你可以自己训练一个模型&#xff0c;或者…

响应式处理-一篇打尽

纯pc端响应式 pc端平常用到的响应式布局 大致就如下三种&#xff0c;当然也会有其他方法&#xff0c;欢迎评论区补充 将div height、width设置成100% flex布局 flex布局主要是将flex-wrap: wrap&#xff0c; 最后&#xff0c;你可以通过给子元素设置 flex 属性来控制它们的…

构建全面的无障碍学习环境:科技之光,照亮学习之旅

在信息与科技日益发展的当下&#xff0c;为所有人群提供一个包容和平等的学习环境显得尤为重要&#xff0c;特别是对于盲人朋友而言&#xff0c;无障碍学习环境的构建成为了一项亟待关注与深化的课题。一款名为“蝙蝠避障”的辅助软件&#xff0c;以其创新的设计理念与实用功能…

Excel 按顺序去重再编号

Excel的A有重复数据&#xff1a; A1Cow2Chicken3Horse4Butterfly5Cow 现在要去除重复&#xff0c;用自然数按顺序进行编号&#xff0c;结果写在相邻列&#xff1a; AB1Cow12Chicken23Horse34Butterfly45Cow1 使用 SPL XLL&#xff0c;输入公式并向下拖&#xff1a; spl(&q…

云平台的安全能力提升解决方案

提升云平台的安全能力是确保数据和服务安全的关键步骤。针对大型云平台所面临的云上安全建设问题&#xff0c;安全狗提供完整的一站式云安全解决方案&#xff0c;充分匹配云平台安全管理方的需求和云租户的安全需求。协助大型云平台建设全网安全态势感知、统一风险管理、统一资…

Zabbix-agents (windows环境)安装及配置

目录 一. 简介 Zabbix 服务端 1. Zabbix 服务器&#xff08;Server&#xff09; 2. Zabbix 数据库 3. Zabbix Web 前端 Zabbix 客户端 1. Zabbix 代理&#xff08;Agent&#xff09; 2. 安装和配置 二. 下载 三. 安装 四. 检查是否启动 五. 手动启动方式 六 .创建…

Python面向对象数据库之ZODB使用详解

概要 ZODB(Zope Object Database)是一个纯Python的面向对象数据库。它允许程序员将Python对象以透明的方式存储在数据库中,无需将对象模型转换为关系模型,极大地简化了Python应用的数据持久化工作。 安装 安装ZODB非常简单,可以通过Python的包管理器pip进行安装: pip …

leecode热题100---994:腐烂的橘子

题目&#xff1a; 在给定的 m x n 网格 grid 中&#xff0c;每个单元格可以有以下三个值之一&#xff1a; 值 0 代表空单元格&#xff1b; 值 1 代表新鲜橘子&#xff1b; 值 2 代表腐烂的橘子。 每分钟&#xff0c;腐烂的橘子 周围 4 个方向上相邻 的新鲜橘子都会腐烂。 返回…

ABP.VNET 项目结构

想要了解ABP分层架构&#xff1a;ABP分层架构-CSDN博客 可以看此篇文章 apb.vnet 生成的项目的目录结构 .Application 项目 应用服务用于将领域(业务)逻辑暴露给展现层。 展现层通过传入DTO(数据传输对象)参数来调用应用服务&#xff0c;而应用服务通过领域对象来执行相应的…

webSocket+Node+Js实现在线聊天(包含所有代码)

这篇文章主要介绍了如何使用 webSocket、Node 和 Js 实现在线聊天功能。 重要亮点 &#x1f4bb; 技术选型&#xff1a;使用 Node.js 搭建服务器&#xff0c;利用 Express 框架和 Socket.io 库实现 WebSocket 通信。 &#x1f4c4; 实现思路&#xff1a;通过建立数组存储聊天…

Go语言直接使用Windows的IOCP API写一个echo服务器

Go的标准库中Windows下的网络是使用了IOCP的&#xff0c;参见go源码go/src/runtime/netpoll_windows.go&#xff0c;标准库为了与Epoll、kqueue等不同平台的IO模式使用统一的API&#xff0c;进行了封装。 如果想直接使用Windows的IOCP API编程&#xff0c;比如想按照&#xff…

场内期权怎么开户?佣金手续费最低是多少?

今天期权懂带你了解场内期权怎么开户&#xff1f;佣金手续费最低是多少&#xff1f;我国的首个场内期权是50ETF期权&#xff0c;随着投资者对期权产品日渐熟悉&#xff0c;投资者参与数量与交易量稳步增长。 场内期权怎么开户&#xff1f; 满足资金要求&#xff1a;根据监管要…

DLT645-97/07通信规约 | 报文解析 | 组织报文与解析报文(C++)

文章目录 一、DLT645通信规约1.DLT645-1997通信规约2.DLT645-2007通讯规约3.DLT645-1997与DLT645-2007的区别 二、DLT645通信规约报文解析1.DLT645-1997通信规约报文解析2.DLT645-2007通信规约报文解析 三、C代码组织报文与解析报文 一、DLT645通信规约 DLT645协议&#xff0c;…

LeetCode 131题详解:高效分割回文串的递归与动态规划方法

❤️❤️❤️ 欢迎来到我的博客。希望您能在这里找到既有价值又有趣的内容&#xff0c;和我一起探索、学习和成长。欢迎评论区畅所欲言、享受知识的乐趣&#xff01; 推荐&#xff1a;数据分析螺丝钉的首页 格物致知 终身学习 期待您的关注 导航&#xff1a; LeetCode解锁100…

每日一题(1)

在看一本08年出版的书的时候&#xff0c;看到了这样一个问题&#xff0c;感觉答案很奇怪&#xff1a; public class demo_p22 {public static void main(String args[]){int sCook1,sFish2;//各技能标记character ch1new character();if(ch1.haveSkill(sCook))System.out.print…

大数据量上传FTP

背景 笔者有一个需求是把将近一亿条数据上传到FTP服务器中&#xff0c;这些数据目前是存储在mysql中&#xff0c;是通过关联几张表查询出来的&#xff0c;查询出来的数据结果集一共是6个字段。要求传输的时候拆分成一个个小文件&#xff0c;每个文件大小不能超过500M。我的测试…

谷歌蜘蛛池是什么?

或称为谷歌爬虫池&#xff0c;是一项专门针对谷歌搜索引擎优化&#xff08;SEO&#xff09;的先进技术&#xff0c;这种技术的主要目的是通过建立庞大的网站群体和复杂的链接结构来吸引和维持谷歌的爬虫程序的注意力&#xff0c;其核心是通过这种结构优化&#xff0c;增强特定网…

使用B2M 算法批量将可执行文件转为灰度图像

参考论文 基于二进制文件的 C 语言编译器特征提取及识别 本实验使用 B2M 算法将可执行文件转为灰度图像&#xff0c;可执行文件转为灰度图的流程如图 4-3 所示。将 可执行文件每 8 位读取为一个无符号的的整型常量&#xff0c;一个可执行文件得到一个一维向量&#xff0c; …