[Qt] 信号与槽:深入浅出跨UI与跨线程的信号发送


文章目录

  • 如何自定义信号并使用
    • 自定义信号的步骤
      • 1.使用 `signals` 声明信号
      • 2. 信号的返回值是 `void`
      • 3. 在需要发送信号的地方使用 `emit`
      • 4. 使用 `connect` 链接信号和槽
      • 5. 完整代码示例
      • 总结
  • 如何跨UI发送信号
    • Qt跨UI发送信号机制详解
    • 案例概述
    • Qt 信号与槽机制简介
    • 代码逻辑详解
      • 主窗口 `Widget` 类
      • 对话框 `SetDialog` 类
    • 信号和槽的连接
    • 总结
  • 如何跨线程发送信号
      • 在Qt中如何跨线程发送信号并更新UI
      • 子线程不能直接操作UI
      • 信号与槽机制在跨线程中的作用
      • 示例代码讲解:子线程发送信号更新UI
        • 定义子线程类 `ChildThread`
        • 定义主窗口类 `Widget`
        • 主程序入口 `main.cpp`
      • 代码运行结果
      • 总结

如何自定义信号并使用

在Qt中,自定义信号 是 Qt 核心功能之一,它允许我们在自定义类中定义信号,并通过信号与槽机制实现对象之间的通信。信号一般与事件相关,比如按钮点击、数据更新等。当事件发生时,信号被触发,并通过槽函数来处理事件。

自定义信号的步骤

让我们逐步讲解如何定义和使用自定义信号,基于以下四个关键步骤:

  1. 使用 signals 关键字声明信号
  2. **信号的返回值为 **void
  3. 在需要发送信号的地方使用 emit 关键字
  4. 使用 connect 将信号和槽连接起来

下面我们通过详细的代码示例来展示如何自定义信号。


1.使用 signals 声明信号

自定义信号需要在类的头文件中用 signals: 关键字来声明。信号就像函数的声明,必须包含函数的参数类型和数量。

  • 注意:信号的返回值类型总是 void,并且只定义函数原型,不需要实际的实现。
class MyWidget : public QWidget
{Q_OBJECTpublic:MyWidget(QWidget *parent = nullptr);signals:// 自定义信号,发送一个整型参数void myCustomSignal(int value);private:QPushButton *myButton;
};

在这个示例中,我们声明了一个信号 myCustomSignal(int value)。这个信号会传递一个 int 类型的参数。

2. 信号的返回值是 void

所有的信号返回值类型必须是 void,即使你定义信号时可能习惯给函数返回某个值,但在 Qt 的信号机制中,信号是不返回任何值的。信号只是通知槽函数或其他接收方事件发生,并不会处理返回值。

3. 在需要发送信号的地方使用 emit

一旦定义了信号,下一步就是在程序的合适位置发射信号。我们使用 emit 关键字来发射信号。当一个信号被发射时,它会通知所有连接到该信号的槽函数。

void MyWidget::someFunction()
{int someValue = 42; // 假设这是我们要发送的值emit myCustomSignal(someValue); // 使用emit发射信号
}

这里我们在 someFunction() 函数中,使用 emit myCustomSignal(someValue) 发射了信号 myCustomSignal,并将 someValue 传递给接收方。

4. 使用 connect 链接信号和槽

发射信号后,还需要将信号与槽函数连接起来。我们通过 connect() 函数,将信号和相应的槽函数连接:

connect(senderObject, &SenderClass::signal, receiverObject, &ReceiverClass::slot);

其中:

  • senderObject 是发射信号的对象。
  • SenderClass::signal 是信号名称。
  • receiverObject 是接收信号的对象。
  • ReceiverClass::slot 是槽函数名称。

假设我们要将 myCustomSignal 信号连接到某个槽函数 onCustomSignalReceived,代码如下:

class ReceiverWidget : public QWidget
{Q_OBJECTpublic:ReceiverWidget(QWidget *parent = nullptr);public slots:void onCustomSignalReceived(int value){qDebug() << "Received value:" << value;}
};

然后在 MyWidget 类中:

void MyWidget::setupConnections(ReceiverWidget *receiver)
{// 将自定义信号 myCustomSignal 与 ReceiverWidget 的槽函数 onCustomSignalReceived 连接connect(this, &MyWidget::myCustomSignal, receiver, &ReceiverWidget::onCustomSignalReceived);
}

5. 完整代码示例

我们将以上所有步骤整合到一个完整的示例中。

mywidget.h: 定义发送信号的类 MyWidget

#ifndef MYWIDGET_H
#define MYWIDGET_H#include <QWidget>
#include <QPushButton>class MyWidget : public QWidget
{Q_OBJECTpublic:MyWidget(QWidget *parent = nullptr);signals:// 自定义信号,传递一个整型数值void myCustomSignal(int value);private slots:void handleButtonClick(); // 按钮点击槽函数private:QPushButton *myButton; // 一个按钮
};#endif // MYWIDGET_H

mywidget.cpp: 实现发送信号的类 MyWidget

#include "mywidget.h"
#include <QVBoxLayout>MyWidget::MyWidget(QWidget *parent): QWidget(parent)
{myButton = new QPushButton("Click Me", this);// 布局设置QVBoxLayout *layout = new QVBoxLayout(this);layout->addWidget(myButton);connect(myButton, &QPushButton::clicked, this, &MyWidget::handleButtonClick);
}void MyWidget::handleButtonClick()
{int value = 42; // 示例数值emit myCustomSignal(value); // 发射信号
}

receiverwidget.h: 定义接收信号的类 ReceiverWidget

#ifndef RECEIVERWIDGET_H
#define RECEIVERWIDGET_H#include <QWidget>
#include <QLabel>class ReceiverWidget : public QWidget
{Q_OBJECTpublic:ReceiverWidget(QWidget *parent = nullptr);public slots:void onCustomSignalReceived(int value); // 槽函数private:QLabel *label;
};#endif // RECEIVERWIDGET_H

receiverwidget.cpp: 实现接收信号的类 ReceiverWidget

#include "receiverwidget.h"
#include <QVBoxLayout>ReceiverWidget::ReceiverWidget(QWidget *parent): QWidget(parent)
{label = new QLabel("Waiting for signal...", this);QVBoxLayout *layout = new QVBoxLayout(this);layout->addWidget(label);
}void ReceiverWidget::onCustomSignalReceived(int value)
{label->setText(QString("Received value: %1").arg(value));
}

main.cpp: 主程序入口,创建并连接信号与槽

#include <QApplication>
#include "mywidget.h"
#include "receiverwidget.h"int main(int argc, char *argv[])
{QApplication app(argc, argv);MyWidget senderWidget;ReceiverWidget receiverWidget;// 将信号连接到槽QObject::connect(&senderWidget, &MyWidget::myCustomSignal, &receiverWidget, &ReceiverWidget::onCustomSignalReceived);senderWidget.show();receiverWidget.show();return app.exec();
}

总结

通过以上步骤,您可以在Qt中自定义信号,并在不同对象之间传递数据。具体步骤如下:

  1. 使用 signals: 关键字在类中声明信号。
  2. 信号的返回值必须是 void
  3. 在合适的地方通过 emit 关键字发射信号。
  4. 使用 connect() 函数将信号和槽连接起来,实现对象间的通信。

信号与槽机制是Qt中非常强大的功能,它极大地简化了对象间的通信,特别是在UI开发中,广泛用于按钮点击、数据更新等场景。

如何跨UI发送信号

Qt跨UI发送信号机制详解

在Qt中,信号与槽机制(Signals and Slots)是其核心特性之一。它允许不同对象之间通过发送和接收信号进行通信,而不需要直接引用。一个典型的应用场景是跨界面(UI)传递数据。在这篇文章中,我们将结合一个完整的示例来演示如何通过信号和槽在两个窗口间(WidgetSetDialog)发送和接收数据。

这篇文章主要面向Qt的初学者,带你一步步理解和实现这一机制。


案例概述

我们将实现一个包含两个窗口的小项目:

  • Widget:主窗口,包含一个按钮和一个文本框。点击按钮后,弹出另一个窗口 SetDialog
  • SetDialog:对话框窗口,包含一个按钮,点击按钮后将产生一个数值,并通过信号将该数值发送给主窗口。

目标:在SetDialog窗口中点击按钮,生成数值,并将数值显示在Widget窗口中的文本框中。


Qt 信号与槽机制简介

在Qt中,信号(Signal)是用于通知某个事件发生的机制,而(Slot)是一个可以连接到信号的函数。当信号发射时,与之连接的槽函数就会被调用。

  • 信号的特点:信号本身不包含任何实现,它只是一个接口。信号通常在类中作为 signals: 下的定义。
  • 槽的特点:槽函数可以是普通的成员函数,也可以是lambda表达式。

我们通过connect()函数将信号和槽连接起来。

代码逻辑详解

主窗口 Widget

首先,我们来看主窗口的类定义。

widget.h

#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();private:void on_btnOpen_clicked();private:Ui::Widget *ui;
};
#endif // WIDGET_H

这里的Widget类继承自QWidget,代表主窗口。在这个窗口中,我们声明了一个槽函数 on_btnOpen_clicked(),用于处理按钮点击事件。

接下来我们来看实现部分。

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include "setdialog.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);// 连接按钮点击信号到槽函数connect(ui->btnOpen, &QPushButton::clicked, this, &Widget::on_btnOpen_clicked);
}Widget::~Widget()
{delete ui;
}void Widget::on_btnOpen_clicked()
{SetDialog dlg;// 连接SetDialog中的sig_addOne信号到主窗口的lambda槽函数connect(&dlg, &SetDialog::sig_addOne, [=](int value) {ui->lineEdit->setText(QString::number(value));});// 打开对话框,进入事件循环dlg.exec();
}
  • connect():在构造函数中,我们将btnOpen按钮的点击信号连接到槽函数on_btnOpen_clicked(),以响应点击事件。
  • on_btnOpen_clicked():当点击按钮时,弹出 SetDialog 对话框。同时,我们将SetDialog中的信号sig_addOne连接到主窗口的lambda槽函数,以便接收数值,并将其显示在文本框lineEdit中。

注意:我们使用了lambda表达式作为槽函数,可以方便地在槽中使用局部变量。

对话框 SetDialog

接下来是 SetDialog 类的定义和实现。

setdialog.h

#ifndef SETDIALOG_H
#define SETDIALOG_H#include <QDialog>namespace Ui {
class SetDialog;
}class SetDialog : public QDialog
{Q_OBJECTpublic:explicit SetDialog(QWidget *parent = nullptr);~SetDialog();private slots:void on_btnAdd_clicked();signals:void sig_addOne(int value); // 定义信号private:Ui::SetDialog *ui;
};#endif // SETDIALOG_H
  • 信号定义:在 signals: 关键字下,我们声明了一个名为 sig_addOne(int value) 的信号。这个信号将会发送一个整数。
  • 槽函数on_btnAdd_clicked() 是槽函数,处理对话框中按钮的点击事件。

setdialog.cpp

#include "setdialog.h"
#include "ui_setdialog.h"SetDialog::SetDialog(QWidget *parent): QDialog(parent), ui(new Ui::SetDialog)
{ui->setupUi(this);
}SetDialog::~SetDialog()
{delete ui;
}void SetDialog::on_btnAdd_clicked()
{static int value = 100; // 静态变量,每次点击都会递增emit sig_addOne(value++); // 发射信号,将当前数值传递给主窗口
}
  • emit** 关键字**:在槽函数 on_btnAdd_clicked() 中,我们使用 emit 关键字来发射信号 sig_addOne,并将数值 value 作为参数传递给主窗口。

信号和槽的连接

现在我们来回顾一下信号和槽的连接过程:

  1. Widget类中,我们弹出了SetDialog窗口,并通过connect()SetDialog中的信号sig_addOne连接到主窗口中的槽函数(lambda表达式)。
  2. 当用户在SetDialog窗口中点击按钮后,on_btnAdd_clicked()被调用,并发射sig_addOne信号,传递当前值。
  3. 这个信号被传递回Widget,并触发了与之连接的槽函数(lambda),最终将数值显示在主窗口的文本框中。

总结

我们通过一个简单的示例,演示了如何在Qt中跨窗口(UI)使用信号和槽进行数据传递。这个机制的关键在于:

  • 信号与槽机制提供了一种松耦合的方式,让对象之间无需直接通信即可实现信息传递。
  • 使用 lambda表达式 作为槽函数,可以让代码更加简洁,同时也方便使用局部变量。

这一机制在Qt开发中非常常见,特别是在需要跨界面传递数据或更新UI时,显得尤为重要。

如何跨线程发送信号

在Qt中如何跨线程发送信号并更新UI

在Qt中,UI线程(主线程)负责处理用户界面绘制及事件响应。而子线程的任务则通常是执行一些耗时操作,例如计算或IO操作。由于Qt的UI框架并非线程安全的,子线程不能直接更新UI,因此我们需要通过信号与槽的机制,将子线程的结果传递到UI线程,并在主线程中进行UI更新。


子线程不能直接操作UI

Qt的规则:UI只能在主线程(即UI线程)中更新,子线程(Worker Threads)不能直接操作UI。否则会引发异常或不可预知的错误。

为了解决这个问题,我们可以通过信号与槽机制,将子线程的处理结果发送到主线程,再由主线程完成UI更新。具体的实现步骤如下:

  1. 在子线程中定义并发射信号。
  2. 在主线程中通过槽函数接收信号并更新UI。

信号与槽机制在跨线程中的作用

信号与槽机制是Qt框架中核心的通信机制。它不仅适用于单线程程序,还能在线程之间进行数据的传递。在跨线程通信时,信号可以在子线程中发射,而槽函数可以在主线程中执行,因此我们可以通过这个机制在子线程与UI线程之间进行安全通信。


示例代码讲解:子线程发送信号更新UI

我们通过一个简单的例子来演示如何实现跨线程信号发送并更新UI。具体实现效果如图所示:

  • 一个按钮用于启动子线程。
  • 子线程执行一些逻辑处理后,将结果通过信号发送到主线程。
  • 主线程接收到信号后,更新界面上的QLineEdit控件。
定义子线程类 ChildThread

首先,我们定义一个子线程类 ChildThread,它继承自 QThread。在该类中,我们声明一个自定义信号 sig_SendToUI,用于将子线程的处理结果传递给主线程。

#ifndef CHILDTHREAD_H
#define CHILDTHREAD_H#include <QThread>
#include <string>using namespace std;// 自定义结构体用于传递数据
struct Score
{int id;int age;string name;
};class ChildThread : public QThread
{Q_OBJECTpublic:ChildThread();protected:void run() override; // 重写QThread的run()函数,定义线程执行逻辑signals:void sig_SendToUI(Score score); // 信号:发送到UI线程
};#endif // CHILDTHREAD_H

run() 方法中,模拟一些处理逻辑,例如设置用户的 nameidage,并在处理完成后,发射信号将数据传递给主线程。

#include "childthread.h"
#include <QDebug>ChildThread::ChildThread()
{// 非基础类型参数注册qRegisterMetaType<Score>("Score");
}void ChildThread::run()
{Score s;s.name = "kevin";s.id = 1001;s.age = 19;emit sig_SendToUI(s); // 发射信号,将Score结构体传递给主线程
}
定义主窗口类 Widget

接下来,我们定义主窗口类 Widget,其中有一个按钮用于触发子线程,一个QLineEdit用于显示子线程处理结果。

#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include "childthread.h"QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();private slots:void on_btnUpdate_clicked(); // 点击按钮启动子线程void showInfo(Score s);      // 显示子线程传递的数据private:Ui::Widget *ui;
};#endif // WIDGET_H

on_btnUpdate_clicked() 槽函数中,我们创建并启动子线程。同时通过 connect() 将子线程的信号 sig_SendToUI 连接到主线程的槽函数 showInfo()

#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include "childthread.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);// 连接按钮点击信号到槽函数connect(ui->btnUpdate, &QPushButton::clicked, this, &Widget::on_btnUpdate_clicked);
}Widget::~Widget()
{delete ui;
}void Widget::on_btnUpdate_clicked()
{ChildThread* ch = new ChildThread(); // 创建子线程对象// 连接子线程信号到UI线程槽函数 showInfoconnect(ch, &ChildThread::sig_SendToUI, this, &Widget::showInfo);ch->start(); // 启动子线程
}void Widget::showInfo(Score s)
{// 将子线程传递的数据信息显示在lineEdit控件上string info = s.name + " id = " + to_string(s.id) + " age = " + to_string(s.age);ui->lineEdit->setText(QString::fromStdString(info));
}
  • on_btnUpdate_clicked():点击按钮后,创建并启动子线程 ChildThread,并连接信号 sig_SendToUI 到槽函数 showInfo()
  • showInfo(Score s):该槽函数接收子线程传递的 Score 结构体,并将其显示在 QLineEdit 控件上。
主程序入口 main.cpp

最后,我们编写 main.cpp 文件,启动整个应用程序。

#include "widget.h"
#include <QApplication>int main(int argc, char *argv[])
{QApplication a(argc, argv);Widget w;w.show();return a.exec();
}

代码运行结果

通过点击按钮,启动子线程,子线程完成数据处理后通过信号传递结果给主线程,主线程接收到信号后更新UI。

  • 按钮点击后,子线程发射信号,主线程接收到Score结构体后,将信息 “kevin id = 1001 age = 19” 显示在QLineEdit控件中,如下图所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

总结

在Qt中,子线程不能直接操作UI,必须通过信号与槽机制来进行跨线程通信。在本文中,我们通过一个简单的例子详细讲解了如何通过信号与槽机制,在子线程中处理数据并将结果传递到UI线程进行更新。主要步骤包括:

  1. 定义子线程类,在子线程中发射信号。
  2. 定义主窗口类,通过槽函数接收子线程信号并更新UI。
  3. 使用connect()连接信号与槽,确保跨线程的安全通信。

通过这样的方式,我们可以轻松实现Qt中的跨线程通信,避免了线程不安全操作带来的问题。

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

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

相关文章

进程通讯方式区别(从不同角度看)

*常用到的不同主机间进程通讯&#xff1a;Socket。比如&#xff1a;host和引擎间socket指令通讯、分派和复判之间指令通讯&#xff1b; *共享内存&#xff1a;在Windows系统中&#xff0c;共享内存的实现通常有以下几种方式&#xff1a; 1.内存映射文件(最常用)&#xff1a;(…

计算机毕业设计 医院预约挂号系统的设计与实现 Python毕业设计 Python毕业设计选题【附源码+安装调试】

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

【01】手把手教你0基础部署SpringCloud微服务商城教学-Mybatis篇(上)

序言&#xff1a; 微服务是一种软件架构风格&#xff0c;它是以专注于单一职责的很多小型项目为基础&#xff0c;组合出复杂的大型应用。 想学习SpringCloud搭建项目&#xff0c;首先我们需要学习的就是Mybatis和Docker。 大家在日常开发中应该能发现&#xff0c;单表的CRUD…

没人告诉你的职场人情世故

看到前同事在群里分享的新年开工遭遇&#xff0c;真是让人感同身受。 第一天就遇到挫折&#xff0c;因为工作做得太快、太早交付&#xff0c;结果反被领导批评&#xff0c;还得重做&#xff0c;头大如斗。这不就提醒我们得时时刻刻记着职场里的那些不成文的规矩吗&#xff1f;…

【C++】常用数据结构纲要(简易版)

非静无以成学。——诸葛亮 数据结构概括 1、什么是数据结构呢&#xff1f;2、讲述过的结构2、1、前言2、2、树->二叉树->两种平衡二叉树2、3、单链表->双链表->带有哨兵位的链表 3、B树3、1、概念及图示3、2、B树数据处理3、2、1、查找3、2、2、插入 4、哈希表4、1…

测试点总结 | 搜索功能如何测试?

这里仅针对通用搜索框的常见测试点进行总结分享&#xff0c;实际工作中需结合搜索功能的背景业务需求及其他依赖条件来综合设计测试点。 一、功能实现部分 &#xff08;1&#xff09;如果支持模糊查询&#xff0c;搜索名称中任意一个字符是否能搜索到 对于支持模糊查询的搜索…

猫头虎分享已解决Bug || AssertionError: Torch not compiled with CUDA enabled 解决方案

&#x1f42f; 猫头虎分享已解决Bug || AssertionError: Torch not compiled with CUDA enabled 解决方案 摘要 今天猫头虎收到粉丝提问&#xff1a;“猫哥&#xff0c;我在使用 PyTorch 进行 AI大模型训练 时&#xff0c;出现了 AssertionError: Torch not compiled with CUD…

10.9 LeetCode 3289 0001 3295

思路&#xff1a; 1、用数组下标来表示是否出现过&#xff0c;初始为 0&#xff0c;出现则加 1&#xff0c;判断大于等于 2 的数字即为多次出现。 2、先将数组排序&#xff0c;依次遍历排序过的数组&#xff0c;若第 i 位与第 i1 位相等&#xff0c;则说明是重复数字。 class …

mybatis解析异常

1.问题现象 Caused by: org.springframework.dao.TransientDataAccessResourceException: Error attempting to get column sale_id from result set. Cause: java.sql.SQLException: Cannot convert value from column 19 to TIMESTAMP. 造成原因:需要给表中中增加字段以满…

审稿人喜欢什么样的Novelty?

在学术出版的世界里&#xff0c;“novelty”&#xff08;创新性&#xff09;是一个被频繁提及的词汇。它似乎是衡量一篇文章价值的黄金标准。然而&#xff0c;当我们深入挖掘这个概念时&#xff0c;会发现所谓的创新性并不是那么绝对。今天&#xff0c;我们就来聊聊审稿人眼中的…

No.15 笔记 | CSRF 跨站请求伪造

目录 一、基础知识 &#xff08;一&#xff09;cookie 和 session、同源策略 &#xff08;二&#xff09;CSRF 原理 二、CSRF 类型 &#xff08;一&#xff09;GET 类型 &#xff08;二&#xff09;POST 类型 三、CSRF 实例讲解 &#xff08;一&#xff09;真实案例 &am…

软件测试学习笔记丨数据库进阶及redis数据库

本文转自测试人社区&#xff0c;原文链接&#xff1a;https://ceshiren.com/t/topic/32358 一、数据库进阶 1.1 MySQL中SQL执行原理 1. SQL语句执行过程 2. Server组件 连接器&#xff1a;连接管理&#xff0c;权限验证查询缓存&#xff1a;命中直接返回结果分析器&#xff…

秋天来临,猫咪又到换毛季,掉毛严重怎么办?宠物空气净化器有用吗?

秋天到了&#xff0c;新一轮的宠物换毛季又来了。谁能想到这只胖猫和之前刚接回来时的皮包骨小猫是同一只&#xff01;除了养了一年长了些肉外&#xff0c;更多的都是换毛季掉毛”膨胀“的。每天下班回家都要搞卫生&#xff0c;家里衣服上、地板上&#xff0c;目光所及之处都有…

acwing:1576. 再次树遍历

打卡一道有意义的题。 题签&#xff1a; 通过使用栈可以以非递归方式实现二叉树的中序遍历。 例如&#xff0c;假设遍历一个如下图所示的 66 节点的二叉树&#xff08;节点编号从 11 到 66&#xff09;。 则堆栈操作为&#xff1a;push(1); push(2); push(3); pop(); pop(); pu…

智能配音软件哪款好?分享5个搞怪软件

想要让视频或社交媒体内容更加生动有趣&#xff1f;搞笑配音软件是个不错的选择。 无论是嘻哈风格的视频&#xff0c;还是搞怪的段子&#xff0c;合适的配音都能让内容增色不少。 今天&#xff0c;就让我们来探索六个文字配音软件&#xff0c;它们不仅能帮你实现搞笑配音&…

H5如何做性能测试?

说起H5性能测试&#xff0c;可能许多同学有所耳闻&#xff0c;但是不知道该如何去做性能测试&#xff0c;或者不知道H5应该关注哪些性能指标。今天我们就来看下。希望阅读本文后&#xff0c;能够有所了解。 常用指标 1、H5性能相关参数介绍 白屏时间&#xff1a;用户首次看到…

L16171819 【哈工大_操作系统】进程同步与信号量信号量临界区保护信号量的代码实现死锁处理

L2.9 进程同步与信号量 让进程走走停停&#xff0c;实现进程同步。 1.、信号量的定义 生产者Producer需要判断是否还有空闲缓冲区生产资源&#xff0c;所以定义一个标志empty&#xff0c;初始值为最大可用资源数&#xff0c;在开头维护&#xff1b;同时&#xff0c;在消费者…

3-GPIO八大输出模式 推挽输出 与 开漏输出

推挽输出 与 开漏输出 GPIO有八大输出模式 下图为每个GPIO口的基本结构&#xff1a; 通过这张图来学习 最右侧是I/O引脚&#xff0c;是从STM32引脚到GPIO口的导线&#xff0c;与其他芯片进行连接的线。 芯片内部电路所能承受的电压有限&#xff0c;当未知的静电进入GPIO口&a…

selenium:Select类操作复选框和下拉框(7)

复选框/下拉框操作的Select类 主要使用selinium中的类Select来模拟选择网页上的下拉框或者复选框中的内容&#xff0c;使用前先导入 from selenium.webdriver.support.ui import Select 主要方法如下&#xff1a; 函数 功能 select_by_value 根据复选框/下拉框的值选择 se…

视觉检测系统实时识别工地安全帽佩戴情况

在建筑工地上&#xff0c;工人佩戴安全帽是确保施工安全的基本措施。然而&#xff0c;工人有时因疏忽或其他原因未能及时佩戴安全帽&#xff0c;这可能导致严重的安全隐患。传统的人工监督往往无法实现对工地的全覆盖或全天候监控&#xff0c;效率低下&#xff0c;容易出现漏检…