<Qt> 信号和槽

目录

一、信号和槽概述

二、信号和槽的使用​​​​​​

(一)connect函数

(二)实现一个点击按钮关闭窗口的功能

(三)再谈connect

三、自定义槽函数

四、自定义信号

五、带参数的信号和槽

六、信号和槽存在的意义

七、信号和槽的断开

八、使用 Lambda 表达式来定义槽函数


一、信号和槽概述

        “信号”这个词在我们的日常生活中随处可见,比如:信号灯变绿,我们行人进行通行、鸡叫表示天亮了、下课铃响了代表着下课了、王者连跪表示我们该充钱了等等信号这个概念,在我们的生活中随处可见。从以上的例子中我们仔细观察的话其实是会发现,每一个信号后面都对应着我们的一个动作,比如:绿灯了,我们要通过人行道、鸡叫了我们要起床了、下课了我们要去干饭、王者连跪了我们要打开微信充钱…这些信号后面伴随的动作是怎么来到?或者说我们怎么知道接收到这些信号后该做什么?当然是生活经验、和老师学校教的反正不可能是临时起意嘛!换而言之就是这些信号后面代表的动作都是我们早已知道的!

        在Linux系统中,我们也介绍了信号的产生、信号的检测以及信号的处理机制,它就是系统内部的通知机制,也可以是一种进程间通信的方式。在系统中有很多信号,我们可以通过signal()函数捕捉信号,重写一个信号处理函数。在Qt中的信号也和系统中的信号有相似之处。

在QT中也是同理,信号就代表着用户发出了一个指令,而槽就代表着对于这个指令的处理方法!


  • 在Qt中,用户和控件的每次交互过程称为一个事件。比如"用户点击按钮"是一个事件,"用户关闭窗口"也是一个事件。每个事件都会发出一个信号,例如用户点击按钮会发出"按钮被点击"的信号,用户关闭窗口会发出"窗口被关闭"的信号。
  • Qt中的所有控件都具有接收信号的能力,一个控件还可以接收多个不同的信号。对于接收到的每个信号,控件都会做出相应的响应动作。例如,按钮所在的窗口接收到"按钮被点击"的信号后,会做出"关闭自己"的响应动作;再比如输入框自己接收到"输入框被点击"的信号后,会做出"显示闪烁的光标,等待用户输入数据"的响应动作。在Qt中,对信号做出的响应动作就称之为槽。
  • 信号和槽是Qt特有的消息传输机制,它能将相互独立的控件关联起来。比如,"按钮"和"窗口"本身是两个独立的控件,点击"按钮"并不会对"窗口"造成任何影响。通过信号和槽机制,可以将"按钮"和"窗口"关联起来,实现"点击按钮会使窗口关闭"的效果。

信号的本质
信号是由于用户对窗口或控件进行了某些操作,导致窗口或控件产生了某个特定事件,这时Qt对应的窗口类会发出某个信号,以此对用户的操作做出反应。因此,信号的本质就是事件。如:

  • 按钮单击、双击
  • 窗口刷新
  • 鼠标移动、鼠标按下、鼠标释放,
  • 键盘输入

在 Qt 中信号是通过什么形式呈现给使用者呢?

  • 我们对哪个窗口进行操作,哪个窗口就可以捕捉到这些被触发的事件。
  • 对于使用者来说触发了一个事件我们就可以得到Qt框架给我们发出的某个特定信号。
  • 信号的呈现形式就是函数,也就是说某个事件产生了,Qt 框架就会调用某个对应的信号函数,通知使用者。

槽的本质

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

说明
(1) 信号和槽机制底层是通过函数间的相互调用实现的。每个信号都可以用函数来表示,称为信号函数;每个槽也可以用函数表示,称为槽函数。 例如: "按钮被按下"这个信号可以用clicked()函数表示,"窗口关闭"这个槽可以用close()函数表示,假如使用信号和槽机制实现: "点击按钮会关闭窗口"的功能,其实就是clicked()函数调用close()函数的效果。

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

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

信号函数的定义是Qt自动在编译程序之前生成的.编写Qt应用程序的程序员无需关注。这种自动生成代码的机制称为元编程(Meta Programming) .这种操作在很多场景中都能见到。


Qt中的信号也要涉及信号三要素:信号源、信号类型和信号处理方式:

  • 信号源:Qt中的信号是由某个控件发出的,Linux系统中可以是一个进程发送的信号。
  • 信号类型:用户不同的操作会触发不同的信号,例如点击按钮就会触发点击信号(clicked)、在输入框中移动光标也会触发相应的信号,我们写这样的GUI程序就是为了和用户交互,所以必须知道当前用户的具体操作,通过不同的操作进行不同的处理
  • 信号处理方式:在Qt中就引入了一个概念就是slot),说白了就是一个函数,再使用connect这样的函数把一个信号和一个槽关联起来,之后只要触发了信号,Qt就会自动执行槽函数,这种槽函数本质上也是一种回调函数

所以在Qt中一定要先关联信号和槽,也就是先要有槽函数并使用connect函数将二者关联起来,然后再触发这个信号,顺序不能颠倒

二、信号和槽的使用​​​​​​

(一)connect函数

上面既然说了,信号的三要素是:发送源、信号类型、处理方法,可是对于QT来说,它怎么知道哪一个信号该调用哪一个处理方法呢?也就是说,对于QT来说,它怎么知道某个信号该调用哪个处理方法呢?
当然,是我们开发人员提前将信号与处理方法之间的关系绑定好,到时候QT直接进行调用即可!

在 Qt 中,QObject类 中提供了一个静态成员函数 connect() ,该函数专门用来关联指定的信号函数和槽函数。

关于QObject类:
QObject类实际上是QT中所有内置类型的祖宗类!也就是说在QT中大部分类要么直接继承自QObject类,要么间接继承自QObject类,比如 QWidget 就继承自 QObject。与Java中的继承十分相似!
在QT中大概的继承关系如下(不准确的继承):

以下是 connect 的函数原型:

connect (const QObject *sender,const char *signal,const QObject *receiver,const char *method,Qt::ConnectionType type = Qt::AutoConnection)sender:   哪个控件发出的信号
signal:   发出的什么信号(信号函数) 
receiver: 谁接收这个信号
method:   怎么处理这个信号(接收信号的槽函数)
type:     用户指定关联方式,常用默认值,一般不需要手动设置.

connect 要求第一个参数和第二个参数是匹配的,比如第一个参数的类型如果是 QPushButton* ,那么第二个参数的信号必须是 QPushButton 内置的信号(父类的信号),不能是其他的一个类。

(二)实现一个点击按钮关闭窗口的功能

我们在窗口上设置一个按钮,当我们按下这个按钮就可关闭窗口。

分析: 当用户按下按钮控件过后,实际上是向QT发送了一个叫clicked的信号,为此我们现在要做的就是捕捉该信号,为该信号关联一个关闭函数,也就是对应Widget类中的 close() 函数

具体代码如下:

#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对象button->setText("关闭"); // 设置按钮上的文本button->move(200, 200); // 设置按钮的位置connect(button, &QPushButton::clicked, this, &Widget::close);
}Widget::~Widget()
{delete ui;
}
  1. 创建一个QPushButton对象,想要点击这个按钮后就关闭这个窗口,发出信号的一定是QPushButton对象。
  2. 发出的信号一定是QPushButton内置的clicked信号。
  3. 要交给的对象就是this也就是Widget对象。
  4. 处理函数就可以使用Widget继承的QWidget内置的close()槽函数。

注意这里是 clicked,而不是 click:

运行程序后,点击按钮成功关闭窗口: 

(三)再谈connect

但是对以上的代码我们有两个疑问?

我们怎么知道点击一下按钮就会发出一个clicked信号?或者我们怎么知道QT有哪些信号或槽函数?

答:多看官方文档;当我们不了解某一个知识点的时候,官方文档是最好的学习工具,况且QT自己在也已经内置了官方文档:

如果我们要查找某一个类的信号的话,我们可以直接对这个类进行搜索,当然可能这个类中并没有包含整个信号,那么我们可以对这个类的父类、祖宗类进行搜索,这样的话我们一定可以找到这个信号,就比如上图的clicked信号,在QPushButton中就没有,但是在其父类QAbstractButton就存在:

找了一遍后发现没有找到clicked这样的信号,既然这样就要向它的父类中查找。 

QPushButton的父类就是QAbstractButton,通过类名我们也可以得知这是一个抽象类,在这个页面向下查找就找到了Signals这块,这里就有clicked等信号,也就类似于函数。

 对于槽函数我们也是如此:


在查看官方文档的时候,connect的原型如下 :

 第一个和第三个参数我们传入的是 QObject 子类指针,所以没有问题,但是第二个和第四个参数传入的是函数指针,但是为什么使用 const char* 来接收呢?( 并且这在C++中是绝对禁止的!) 就拿上面connect的例子举例,clicked的类型应该是void ( * ) () ,而close的类型应该是 bool ( * ) (),可是为什么这两个函数指针类型可以直接赋值给 const char*? 这两个的意义可不一样。

  • 原因就是这是旧版本的 connect 函数声明,是在对于信号传参的时候需要用SIGNAL宏修饰一下,对于槽函数传参的时候需要使用SLOT宏修饰一下,通过宏替换就可以把函数指针转换成 char*,这样就不会有类型不匹配的问题了。
  • 官方文档里的这个函数声明是旧版本的 connect 声明 ,正确写法是:
  • connect(button1, SIGNAL(&QPushButton::clicked), this, SLOT(&Widget::close))

从 Qt5 开始,对该写法进行了简化,不再需要写这两个宏了,所以上面的代码才能直接传入函数指针。Qt5 开始,给 connect 提供了重载版本,第二个参数和第四个参数成了泛型参数,允许传入任意类型的函数指针了。
点击 ctrl 然后鼠标点击 connect ,跳转到源码中查看:

这样的话,对于一个函数指针来说,它就不用再使用SIGNAL宏和SLOT宏来进行修饰了,根据模板的特性,它会自动推导传递过来的函数指针的类型,极其方便!同时新版的connect还使用了“类型萃取”技术,它可以帮助我们检查参数,发送源控件与信号类型是否匹配:若传入的第一个参数和第二个参数,或者第三个参数和第四个参数不匹配,(不匹配是指:2、4参数传入的函数指针,不是1、3参数的成员函数的指针。)代码出现编译错误。

三、自定义槽函数

纯代码

虽然QT已经给我们内置了许多对应的槽函数,但是在我们实际的开发中,我们更多的情况下,是结合自己的业务场景,针对对应的信号量身定做一个槽函数,也就是自定义槽函数。

自定义参函数的语法:

  1. 所谓的 slot 就是一个普通的成员函数,所以所谓的自定义一个槽函数,操作过程和自定义一个普通的成员函数没啥区别。
  2. 该成员函数需要使用public signals、private signals、protected signals来进行修饰。(在QT 5及以上版本中可以不用signal修饰)

为此,为了演示自定义槽函数,这也是上一篇也用过的,先使用纯代码的方式实现一下自定义槽。我们设计出一个具体的场景:

当用户点击按钮的时候就可以切换窗口标题。

分析: 当用户点击按钮的时候,实际上会发出一个clicked信号,现在对应的处理动作是将窗口标题改为:“捕捉到按钮信号”,为此我们需要捕捉clicked信号,并且自定义处理函数。

  • 还是要先new一个QPushButton对象,把点击信号和槽函数关联起来。
  • 槽函数中就设置为,捕捉到了信号就把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();// 自定义槽函数
//public slots:
//    void handleClicked();void handleClicked(); // 这两者自定义槽函数的方式都是可以的private:Ui::Widget *ui;
};
#endif // WIDGET_H

widget.cpp

#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对象button->setText("按钮"); // 设置按钮上的文本button->move(200, 200); // 设置按钮的位置connect(button, &QPushButton::clicked, this, &Widget::handleClicked);
}Widget::~Widget()
{delete ui;
}void Widget::handleClicked()
{// 按下按钮,修改窗口标题this->setWindowTitle("捕捉到按钮信号");
}

运行程序,查看效果:

窗口标题成功修改: 

图形化界面

在 ui 界面,拖动 pushbutton 到编辑界面然后输入文字后,右键单击该按钮,选择 “转到槽” 

这个窗口为我们提供了 QPushButton 的所有信号,还包括了 QPushButton的父类信号。选择第一个信号:

点击确定或者双击就会跳转到编辑widget.cpp的界面,此时函数的声明和定义都已经自动生成好了,编写代码即可:

【注意】:这种方式是不会出现 connect() 函数的,Qt中除了connect可以连接信号和槽外,还可以通过函数名字的方式来自动连接,如上图所示函数名格式:on_ XXX_ SSS,其中:

  • 以"on"开头,中间使用下划线连接起来;
  • " XXX "表示的是对象名(控件的objectName)。
  • " SSS "表示的是对应的信号。
  • 如: " on_ pushButton_ _clicked() " , pushButton代表的是对象名,clicked 是对应的信号。

点击按钮,改变窗口的标题:


这里名字故意写错,就不能达到我们想要的效果:

这都是Qt中调用connectSlotsByName这个函数触发自动连接信号和槽,这个函数是在自动生成的ui_widget.h中setupUi函数中调用的。 

所以,如果是通过图形化界面创建控件,还是使用第二种方式快速连接信号和槽;如果是通过代码的方式创建控件,还是得手动调用connect函数,原因就是没有调用connectSlotsByName函数。

四、自定义信号

自定义信号很少用到。因为在GUI中,用户的操作行为是可以穷举的,Qt内置的信号已经覆盖到了大部分可能的用户操作,对于某些场景还是需要我们使用自定义信号的!

  • 信号是一种特殊的函数,程序员只需写出函数声明,并告诉Qt(需要使用slots关键字进行修饰),这是一个信号即可。这个函数的定义,是Qt在编译过程中,自动生成的(无法干预)。
  • 信号函数的返回值必须是void,有没有参数都可以,也支持函数重载。
  • 信号可以使用emit关键字进行发射(Qt5 emit不写也行)。

为此,我们根据该场景设计出一个具体的业务场景:

当用户点击按钮就可以切换窗口标题

signals是Qt自己扩展的关键字,qmake会调用代码分析和生成的工具,识别到signals这个关键字时就会把下面的函数声明认为是信号,并自动生成函数定义:

通过connect函数连接信号和槽函数就可以了,但是光连接还是不够的,还需要发送我们自定义的信号,这个emit也是Qt中的关键字,作用就是发送信号:

当点击按钮,就会触发on_pushButton_clicked函数,函数中就会发送mySignal信号,收到信号就会执行handleSignal自定义函数,之后就会修改窗口标题:

五、带参数的信号和槽

在自定义信号和槽函数的时候可以带上参数,并且QT允许重载信号和槽函数。信号在和槽函数进行绑定的时候,主要表现在以下两点:

  • 槽函数的参数类型必须和它绑定的信号的参数类型一致。
  • 槽函数的参数个数可以少于等于它绑定的信号的参数的个数。

参数的类型必须要一致,个数不一致是可以的,但是要求信号的参数的个数必须要比槽的参数多。

通过这一次连接信号和槽,并搭配不同参数就可以实现不同结果,可以让代码复用,就比如:

点击按按钮1,左上角标题改为标题1,点击按钮2,左上角标题改为标题2:

而且信号的参数比槽函数多也是可以的,但是不允许槽函数中的参数比信号中的多,原因就是:

  • 一个槽函数可能绑定多个信号。
  • 如果严格要求就意味着信号绑定到槽的要求变高了。
  • 所以信号的参数个数可以大于槽的参数个数,这样让信号和槽之间的绑定变得更灵活,更多的信号可以绑定到这个槽函数上。
  • 虽然个数不一致,但是槽函数会按照参数顺序拿前N个参数,这样就可以确保槽函数的每个参数都有值。

 还有一点就是,如果想要使用信号和槽的机制,就必须在类的一开始写上Q_OBJECT宏,否则会出现编译错误

当我们转到定义就可以看到这个宏中展开的代码,这里也有很多的宏,也会继续展开,最终实现相应的功能:

六、信号和槽存在的意义

信号和槽最主要就是要解决响应用户的操作,它在现在GUI框架中是一个有特色的机制,但是它的实现就没有那么简洁,但是它还要这样做就是为了:

  • 把触发用户操作的控件 和 处理用户的操作逻辑 解耦合。信号发送者不需要知道发出的信号被哪个对象的槽函数接收,槽函数也不需要知道哪些信号关联了自己,Qt的信号槽机制保证了信号与槽函数的调用。支持信号槽机制的类或者父类必须继承于QObject类。
  • 也想起到多对多的效果,一个信号可以connect到多个槽函数上,一个槽函数也可以被多个信号connect。(实际开发中,这种情况极少)

这就是可以把多个信号和多个槽函数绑定到一起:

缺点:

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

七、信号和槽的断开

可以使用disconnect断开信号和槽的连接。但是大部分情况下,把信号和槽连接好就不用管了,主动断开的情况就是要把信号重新绑定到另一个槽函数上,如果不断开,这个信号发送就会触发两个槽函数。

  • 点击按钮修改标题:

  • 先点击断开原信号按钮,建立新连接,再点击修改标题按钮:

断开连接前,标题修改为旧标题,当点击下方pushButton后,上方原来的信号和槽断开了连接,重新绑定后,再点击上方pushButton,标题修改为新标题。

八、使用 Lambda 表达式来定义槽函数

Qt5在Qt4的基础上提高了信号与槽的灵活性,允许使用任意函数作为槽函数。
但如果想方便的编写槽函数,比如在编写函数时连函数名都不想定义,则可以通过Lambda表达式来达到这个目的。

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

[ capture ] ( params ) opt -> ret { Function body; 
};

局部变量引入方式 [ ]
[ ]:标识一个Lambda表达式的开始。不可省略。

  • 由于使用引用方式捕获对象会有局部变量释放了而Lambda函数还没有被调用的情况。如果执行Lambda函数,那么引用传递方式捕获进来的局部变量的值不可预知。所以绝大多数场合使用的形式为:={}
  • 早期版本的Qt,若要使用Lambda表达式,要在"pro"文件中添加:
    CONFIG += C++11
    

使用 Lambda 表达式的方式填写槽函数。这里的功能是点击一下按钮,按钮就移动到相应位置:

上述代码传值捕获没问题,传引用捕获会崩溃。原因是button局部变量(它指向的空间位于堆区,但其本身是一个局部变量的指针),构造函数结束后button变量即被销毁,生命周期结束,若传引用对这块非法空间进行访问就会造成程序崩溃。

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

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

相关文章

【从零开始实现stm32无刷电机FOC】【实践】【6/7 CMSIS-DSP】

目录 导入CMSIS-DSP库使用CMSIS-DSP 点击查看本文开源的完整FOC工程 CMSIS-DSP库是ARM开源的、对ARM处理器优化的数学库&#xff0c;本文使用了其提供的三角函数、反park变换函数、park变换函数、clarke变换函数、PID控制器。 CMSIS-DSP原始代码仓库是https://github.com/ARM-s…

双领域TOP10优秀安全企业!通付盾入选《嘶吼2024网络安全产业图谱》六大分类14项细分领域

7月16日&#xff0c;嘶吼安全产业研究院正式发布《嘶吼2024网络安全产业图谱》&#xff08;以下简称“图谱”&#xff09;&#xff0c;通过市场调研、数据精析、文献研究及政策参考等多方面的综合分析&#xff0c;全面展示网络安全产业的构成及其重要组成部分&#xff0c;探索网…

MongoDB教程(九):java集成mongoDB

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; 文章目录 引言一、环境准…

网安小贴士(19)入侵检测技术原理与应用

前言 入侵检测技术&#xff08;Intrusion Detection System, 简称IDS&#xff09;是一种用于监测和防止计算机网络中的恶意活动的安全系统。它通过收集系统状态信息、特征提取、建立模型、入侵检测以及反馈更新等步骤&#xff0c;及时检测网络和系统中可能遭受攻击的迹象并发出…

python安装talib库教程

【talib介绍】 Talib介绍 Talib&#xff0c;全称“Technical Analysis Library”&#xff0c;即技术分析库&#xff0c;是一个广泛应用于金融量化领域的Python库。该库由C语言编写&#xff0c;支持Python调用&#xff0c;为投资者、交易员和数据分析师提供了强大的技术分析工…

借力Jersey,铸就卓越RESTful API体验

目录 maven 创建 jersey 项目 运行 支持返回 json 数据对象 1. 引言 在当今数字化时代&#xff0c;API&#xff08;应用程序编程接口&#xff09;已成为连接不同软件系统和服务的桥梁。RESTful API以其简洁、轻量级和易于理解的特点&#xff0c;成为了API设计的首选标准。本…

达梦数据库的系统视图v$arch_file

达梦数据库的系统视图v$arch_file 在达梦数据库中&#xff0c;V$ARCH_FILE 是一个动态性能视图&#xff0c;用于显示当前数据库的归档日志文件信息。这个视图可以帮助数据库管理员监控和管理归档日志文件&#xff0c;确保数据库的备份和恢复过程顺利进行。 查询本地归档日志信…

golang 基础 泛型编程

&#xff08;一&#xff09; 示例1 package _caseimport "fmt"// 定义用户类型的结构体 type user struct {ID int64Name stringAge uint8 }// 定义地址类型的结构体 type address struct {ID intProvince stringCity string }// 集合转列表函数&#…

springboot框架的事务功能

事务回顾 概念 事务是一组操作集合&#xff0c;它是一个不可分割的工作单位&#xff0c;这些操作要么同时成功&#xff0c;要么同时失败。 操作 开启事务~&#xff08;一组操作开始前&#xff0c;开启事务&#xff09;&#xff1a;start transaction / begin; 提交事务~&am…

【python基础】基本数据类型

文章目录 一. Python基本数据类型1. 整数1.1. python的四种进制1.2. 数中的下划线 2. 浮点数3. 复数4. 布尔型5. 运算符5.1. 算术运算符5.2. 比较运算符5.3. 逻辑运算符5.4 运算符优先级 6. 常量 二. 注释三. Python之禅 一. Python基本数据类型 1. 整数 无长度限制&#xff1…

【HTML入门】第十四课 - form表单(下)表单控件们(一)

上一节&#xff0c;我们学习了基本的 form 标签的属性们&#xff0c;我们说过&#xff0c;form表单是一个区域&#xff0c;里面有一些控件元素&#xff0c;这一小节&#xff0c;我们来看一下这些控件们。 目录 1 文本输入框 2 密码输入框 3 数值型输入框 4 文本域输入框 1…

如何为游戏本地化选择语言

为游戏本地化选择合适的语言是一个至关重要的决定&#xff0c;它会极大地影响您的游戏在国际市场上的成功。适当本地化的游戏可以接触到更广泛的受众&#xff0c;提高玩家满意度&#xff0c;并促进销售。以下是在决定将游戏本地化为哪种语言时需要考虑的一些关键因素&#xff1…

Java(二十一)---栈的使用和模拟实现

文章目录 前言1.什么是栈(Stack)?2. 栈的模拟实现3.stack的使用![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/80c82d22f3ee49cfaa2915d1c961573e.png)4.关于栈的oj题4.1.有效的括号4.2.逆波兰表达式4.3.栈的压入、弹出序列4.4.最小栈 前言 前面几篇我们学习了顺序…

UE4-打包游戏,游戏模式,默认关卡

一.打包游戏 注意windows系统无法打包苹果系统的执行包&#xff0c;只能使用苹果系统打包。 打包完之后是一个.exe文件。 打包要点&#xff1a; 1.确定好要操控的角色和生成位置。 2.设置默认加载的关卡和游戏模式。 在这个界面可以配置游戏的默认地图和游戏的模式&#xff0c;…

八,九、 MyBatis 操作数据库 ★ ✔

八&#xff0c;九、 MyBatis 操作数据库 JDBC 操作⽰例回顾1. 什么是MyBatis?2. MyBatis⼊⻔2.1 准备⼯作2.1.1 创建⼯程 2.1.2 数据准备2.2 配置数据库连接字符串2.3 写持久层代码 ★2.4 单元测试 ★ 使⽤Idea ⾃动⽣成测试类3. MyBatis的基础操作3.1 打印⽇志 ★3.2 参数传递…

26.js事件

事件&#xff1a;用户行为 事件三要素: 事件源 事件类型 事件处理函数 事件绑定 语法1&#xff1a;dom 0级&#xff08;dom 0级指最原始的&#xff0c;在dom标准被正式定义之前的事件处理方式&#xff0c;事件处理函数通常在<script>标签中或直接在HTML元素的事件属性上…

轻量级文本编辑器 | Notepad-- v2.17 官方版

软件简介 Notepad--是一款国产的跨平台轻量级文本编辑器&#xff0c;旨在作为 Notepad 的替代品。它使用 C 编写&#xff0c;支持 Windows、Mac、Linux 等多种操作系统。 鉴于某些Notepad竞品作者的不当言论&#xff0c;Notepad--的意义在于&#xff1a;减少一点错误言论&…

应用最优化方法及MATLAB实现——第5章代码实现

一、概述 继上一章代码后&#xff0c;这篇主要是针对于第5章代码的实现。部分代码有更改&#xff0c;会在下面说明&#xff0c;程序运行结果跟书中不完全一样&#xff0c;因为部分参数&#xff0c;书中并没有给出其在运行时设置的值&#xff0c;所以我根据我自己的调试进行了设…

SwiftUI 6.0(Xcode 16)新 PreviewModifier 协议让预览调试如虎添翼

概览 用 SwiftUI 框架开发过应用的小伙伴们都知道&#xff0c;SwiftUI 中的视图由各种属性和绑定“扑朔迷离”的缠绕在一起&#xff0c;自成体系。 想要在 Xcode 预览中泰然处之的调试 SwiftUI 视图有时并不是件容易的事。其中&#xff0c;最让人秃头码农们头疼的恐怕就要数如…

httpx,一个网络请求的 Python 新宠儿

大家好&#xff01;我是爱摸鱼的小鸿&#xff0c;关注我&#xff0c;收看每期的编程干货。 一个简单的库&#xff0c;也许能够开启我们的智慧之门&#xff0c; 一个普通的方法&#xff0c;也许能在危急时刻挽救我们于水深火热&#xff0c; 一个新颖的思维方式&#xff0c;也许能…