1.1.1 Qt信号槽之connect与disconnect介绍

news2024/11/17 23:32:10

关于Qt信号槽中connect与disconnect介绍

首先我们要知道,如果想要使用Qt中的信号槽机制, 那么必须继承QObject类,因为QObject类中包含了信号槽的一系列操作,今天我们来讲解的是信号与槽怎么建立连接以及断开连接

一、connect

QMetaObject::Connection QObject::connect(param p1, param p2, …);

Qt中关于信号槽的connect给出了五种方式,不同connect方法的主要区别是参数的个数及参数类型不同,用来满足不同的场景,但相同的是他们的返回值Connection类型,关于Connection在上一篇文章中也已经描述过,主要用于表示当前信号与当前槽函数连接的句柄,可以通过这个句柄来判断当前连接是否成功、也可以通过此句柄来定位到当时的连接,以此来断开对应信号与槽的连接。


下面分别来讲解这几种不同方式的用法。

1.1 Qt4中通常写法

connect(const QObject *sender, const char * signal, const QObject *receiver, const char * method , Qt::ConnectionType type = Qt::AutoConnection)

// 示例(输入框文字变动实时显示到Label上); 
QLabel *label = new QLabel;
QLineEdit *lineEdit = new QLineEdit;
QObject::connect(lineEdit, SIGNAL(textChanged(const QString&)),
	label, SLOT(setText(const QString&)));

用法:
此方式是Qt4中通用使用的方式,参数分别为信号发送的对象,信号函数,信号接收对象,信号触发的槽函数,这种写法就必须要求给==信号和槽函数前加上宏SIGNAL()和SLOT()==来指定,且信号和槽函数后面必须且只需要加上参数类型(如果没有参数,为空就好,如果有参数,切记只需要填每个参数的类型,不需要具体的变量名称,否则连接失败),因为我们看到此方式信号和槽函数对应的参数类型为 const char*,你可以随意填任意的字符串,但我们必须要按照这种方式,Qt内部会根据宏展开具体的操作,所以这种方式Qt不会帮你检查是否填写错误,尽管你随便填也可以编译成功,只不过连接不上罢了,因为参数都是字符串,编译期是不检查字符串是否匹配,所以这种方式你必须自己检查宏SIGNAL()和SLOT()里面的内容是否正确。

SIGNAL()和SLOT()宏,它们会将信号/槽函数及其参数转化为const char *类型。

使用场景:
此方式用于Qt4的项目中,Qt5为了兼容Qt4保留了此方式,此外因为很多落后且还在更新的项目仍在采用Qt4版本的库,同时由于团队合作的方式,有些人习惯于Qt4的写法,有些人则偏向于Qt5,所以项目中正常这两种方式都会存在。

这种方式其实我们在上篇文章中与Qt5的正常connect方式做了对比,使用过程中需要注意SIGNAL()和SLOT()里面的内容是否正确,而且就算出错编译器也不会报错,所以会经常出现信号槽连接失败的问题。


1.2 Qt4中不常用法

connect(const QObject *sender, const QMetaMethod &signal, const QObject *receiver, const QMetaMethod &method, Qt::ConnectionType type = Qt::AutoConnection)

等同于

connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)

区别在于这里的信号和槽函数为 const QMetaMethod & 类型,QMetaMethod 是Qt提供的一个专门的类型,可以用于获取一个对象成员函数的元数据,可以获取此成员函数的类型(普通成员函数/信号/槽函数/构造函数)、签名(类似"setValue(double)"),参数类型,参数名称,返回类型,访问权限等等。
正常情况很少用到,大家可能也没有怎么接触过这种方法,这里大家可以认为等同于上面的方式,也就是使用QMetaMethod对信号/槽函数进行指定。
这种方式其实也是为了兼容Qt4中的写法,日常大家可以跳过这种方式。

下面给大家举个例子:

QMetaMethod signalMethod = QMetaMethod::fromSignal(&MyWgtA::signalA);
int methodIndex = m_wgtB->metaObject()->indexOfMethod("onSlotB()");
// 防止找不到;
if (methodIndex != -1)
{
	QMetaMethod slotMethod = m_wgtB->metaObject()->method(methodIndex);
    connect(m_wgtA, signalMethod, m_wgtB, slotMethod);
}

// delete;
QMetaMethod参考
https://blog.csdn.net/qq_21291397/article/details/104774421


1.3 Qt4中信号接收对象省略法

connect(const QObject *sender, const char *signal, const char *method, Qt::ConnectionType type = Qt::AutoConnection) const

等同于

connect(sender, signal, this, method, type)

此种方法在调用过程中省略了信号接收的对象指针,默认为this(调用对象本身),也就是在哪个类里面调用connect,就是连接这个类的信号/槽函数。
如下方代码中,MyWgtA类的signalA(QString)信号与MyTestWgt类的槽函数onSlotTest()进行连接。

void MyTestWgt::initConnections()
{
    connect(m_wgtA, SIGNAL(signalA(QString)), SLOT(onSlotTest()));
}

1.4 Qt5通常用法

connect(const QObject *sender, PointerToMemberFunction signal, const QObject *receiver, PointerToMemberFunction method, Qt::ConnectionType type = Qt::AutoConnection)

// 示例(输入框文字变动实时显示到Label上); 
QLabel *label = new QLabel;
QLineEdit *lineEdit = new QLineEdit;
QObject::connect(lineEdit, &QLineEdit::textChanged,
               label,  &QLabel::setText);

参数中的信号必须在头文件中使用signals显示声明,槽函数可以是任何一个成员函数(如果是外部触发必须是public权限),不需要进行slots显示声明(Qt4中必须得有),所以Qt5这种方式真的方便了很多,上一篇文章中,在给大家分析信号槽连接失败原因的时候,就提到Qt4与Qt5的区别,相信大家看完那篇文章也深有体会。

使用这种方式,connect中可以省略信号和槽的参数,也不需要 SIGNAL()和SLOT() 宏的加持,同时编译的时候也会帮你检查信号和槽函数的参数是否正确(保证信号的参数个数>=槽函数参数的个数,且前置参数类型保持一致),防止程序运行的时候才发现信号槽没连上,这个时候你可能写了一大段代码(包含多个connect连接),还不知道是哪个connect会失败,所以建议使用Qt5的信号槽连接方式。

注意:

使用Qt5这种方式,大家有一点是需要注意,也区别于Qt4,当我们的信号重载的时候,需要配合QOverlord进行使用。

![在这里插入图片描述](https://img-blog.csdnimg.cn/cc894a5fe84846ebb8d4a431aa9f330f.png

我们看到QButtonGroup类的buttonClicked信号是有两个重载的,且参数类型不一样,所以我们在使用的时候需要借助QOverload来指定你是用哪一个信号,可以看到下方的代码,这里的代码是信号发生了重载,如果是槽函数发生了重载,同样需要使用QOverload来指定,确保唯一性。

// 如果参数类型是int;
void MyWidget::onBtnClicked(int btnId)
{}

connect(buttonGroup, QOverload<int>::of(&QButtonGroup::buttonClicked), myWidget, &MyWidget::onBtnClicked);
// 如果参数类型是QAbstractButton*;
void MyWidget::onBtnClicked(QAbstractButton* pBtn)
{}

connect(buttonGroup, QOverload<QAbstractButton *>::of(&QButtonGroup::buttonClicked), myWidget, &MyWidget::onBtnClicked);
      

1.5 Qt5信号直连法

connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)

这种方式较为简单,我称之为信号直连法,这里不需要接收信号的对象,我们看到这里只有三个参数,且第三个参数是一个仿函数,相当于信号发出后直接调用仿函数。

A function can be connected to a given signal if the signal has at least as many argument as the slot. A functor can be connected to a signal if they have exactly the same number of arguments.

The connection will automatically disconnect if the sender is destroyed. However, you should take care that any objects used within the functor are still alive when the signal is emitted.

这里提供了两种方式:

a:仿函数

void someFunction()
{
    // todo;
}

QPushButton *button = new QPushButton;
QObject::connect(button, &QPushButton::clicked, someFunction);

b:Lambda表达式

QPushButton *button = new QPushButton;
QObject::connect(button, &QPushButton::clicked, [=](){
    // todo;
});

1.6 Qt5信号直连法2

connect(const QObject *sender, PointerToMemberFunction signal, const QObject *context, Functor functor, Qt::ConnectionType type = Qt::AutoConnection)

此种用法基本和上面的类似,区别在于此方法有了Qt::ConnectionType参数,可以决定当前槽函数的执行方式。

这里也提供了两种方式:

a:仿函数

void someFunction()
{
    // todo;
}

QPushButton *button = new QPushButton;
QObject::connect(button, &QPushButton::clicked, this, someFunction);

b:Lambda表达式

QPushButton *button = new QPushButton;
QObject::connect(button, &QPushButton::clicked, this, [=](){
    // todo;
});

二、disconnect

上面讲解了信号槽的6种连接方式,现在讲讲断开连接的方法 disconnect
一般我们用的比较多的是信号槽的连接,断开可能用的相对少,有的初学者可能还不知道有disconnect这个方法,以为信号槽连接就完事了,就比如一个按钮点击就是需要触发一个方法来完成一系列的操作,如果你把他断开连接了,那这个按钮不是没用了吗,那不一定哦。正常情况下,我们的信号会和一个槽函数绑定,但是Qt允许一个信号可以绑定多个槽函数,当我们想断开其中一个连接的时候就需要使用disconnect去断开这一个,其他几个连接可以保持正常,当然也可以全部断开。

举个例子哈,比如我们给好友发消息,信号是发送按钮点击这个信号(signalSendBtnClicked),这个信号我们需要做两个操作(也就是一个信号对应两个槽函数),一个是将发送的文字显示到我们的聊天窗口,一个是将内容发送给服务器进行转发,正常情况下是做这样两个操作。
情况一:如果对方断线,那我们只需要把内容显示到我们的界面上,但是不需要发送到服务器,所以这里就需要断开一个槽函数。
情况二:如果我们自己断线了,我们实际上什么也做不了,因为你已经离线了,所以点击发送就无需做任何操作,我们就断开所有对应的槽函数即可。

disconnect 方法相对connect简单一些,操作起来也非常方便灵活,Qt提供了几种方式供我们使用.
需要注意的是connect方法都是静态方法,直接调用即可;而disconnect分为静态方法和成员方法两种调用方式(静态方法我会在前方加上 [static])。

disconnect 方法的返回值是一个bool类型,我们可以根据这个值来判断当前是否成功断开信号槽的连接。


1.1 对应Qt4的通用写法(静态方法)

[static] bool QObject::disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)

a、断开对象A的信号signalA()与对象B的槽函数onSlotB()的连接(正常情况下)

connect怎么写的,disconnect就怎么写,即把之前的connect直接换成disconnect。

// 信号槽连接;
connect(lineEdit, SIGNAL(textChanged(const QString&)),
	label, SLOT(setText(const QString&)));

// 信号槽断开;
bool isSuccess = disconnect(lineEdit, SIGNAL(textChanged(const QString&)),
	label, SLOT(setText(const QString&)));

// 根据返回值判断断开是否成功;	
if(isSuccess)	
{
    qDebug() << "disconnect Success";
}

b、断开对象A所有信号的连接:

只需要填写对象A,其他都默认填写nullptr即可断开此对象所有信号的相关连接。

disconnect(myObjectA, nullptr, nullptr, nullptr);

等价于:

// 成员方法(对应下方1.5);
myObjectA->disconnect();

c、断开对象A的信号signalA()所有的连接:

disconnect(myObjectA, SIGNAL(signalA()), nullptr, nullptr);

等价于:

// 成员方法(对应下方1.5);
myObjectA>disconnect(SIGNAL(signalA()));

d、断开对象A与对象B所有的信号槽连接:

disconnect(myObjectA, nullptr, myReceiverB, nullptr);

等价于:

// 成员方法(对应下方1.6);
myObjectA->disconnect(myReceiverB);

关于此方法中的参数,我们可以通过空指针nullptr来充当通配符,但是sender参数必须非空,因为disconnect只能断开某一个对象相关信号的连接,具体就是上述四种用法。

1.2 对应Qt4的不常用法-QMetaMethod(静态方法)

[static] bool QObject::disconnect(const QObject *sender, const QMetaMethod &signal, const QObject *receiver, const QMetaMethod &method)

此方式就是通过对应信号和槽函数的QMetaMethod来断开对应的连接,这里就不举例说明了,不推荐这种方法。

1.3 connect返回句柄断开法(静态方法)

[static] bool QObject::disconnect(const QMetaObject::Connection &connection)

这种方式需要记录之前信号槽连接的句柄,然后通过此句柄来断开唯一对应的信号与唯一对应的槽函数的连接,也不是很推荐。

m_pBtn = new QToolButton;
// 得到句柄;
QMetaObject::Connection cc = connect(m_pBtn, SIGNAL(clicked()), this, SLOT(onBtnCLicked()));

// 根据connect返回的句柄断开相应的信号槽连接;
disconnect(cc);

1.4 对应Qt5的通用写法(静态方法)

[static] bool QObject::disconnect(const QObject *sender, PointerToMemberFunction signal, const QObject *receiver, PointerToMemberFunction method)

QObject::disconnect(lineEdit, &QLineEdit::textChanged,
                   label,  &QLabel::setText);

这个方式跟1.1基本一致,也是四种写法,可以使用空指针nullptr来充当通配符,就是注意将参数 const char*换成PointerToMemberFunction 类型即可。
即:

SIGNAL(textChanged(const QString&) 换成 &QLineEdit::textChanged
SLOT(setText(const QString&) 换成 &QLabel::setText)

1.5 断开某个对象的信号连接(成员方法)

bool QObject::disconnect(const char *signal = nullptr, const QObject *receiver = nullptr, const char *method = nullptr) const

与1.1类似,也有四种写法。

// a、断开对象A所有信号的连接:
myObjectA->disconnect();

// b、断开对象A的信号signalA()所有的连接:
myObjectA->disconnect(SIGNAL(mySignal()));

// c、对象A与对象B断开所有信号连接:
myObjectA->disconnect(nullptr, myReceiverB, nullptr);

// d、对象A与对象B断开槽函数onSlotB()所有相关信号的连接::
myObjectA->disconnect(nullptr, myReceiverB, SLOT(onSlotB()));

1.6 对象A与对象B断开相关信号的连接(成员方法)

bool QObject::disconnect(const QObject *receiver, const char *method = nullptr) const

与1.5方法有有类似的效果。

// a、对象A与对象B断开所有信号连接:
myObjectA->disconnect(myReceiverB)


// b、对象A与对象B断开槽函数onSlotB()所有相关信号的连接::
myObjectA->disconnect(myReceiverB, SLOT(onSlotB()));

1.7 注意

如果之前信号连接的是仿函数或者Lambda表达式,我们只能使用1.3里面提到的 QMetaObject::Connection 这种方式来断开连接,因为其他方式没有办法进行唯一匹配。


三、信号槽连接注意点

1、一个信号可以连接一个信号;

connect(myObjectA, &ObjectA::signalA, myReceiverB, &ObjectB::signalB);

2、一个信号可以同时连接多个信号/槽函数;

connect(myObjectA, &ObjectA::signalA, myReceiverB, &ObjectB::signalB);
connect(myObjectA, &ObjectA::signalA, myReceiverB, &ObjectB::onSlotB);
connect(myObjectA, &ObjectA::signalA, myReceiverC, &ObjectC::onSlotC);
connect(myObjectA, &ObjectA::signalA, myReceiverD, &ObjectD::onSlotD);

3、多个信号可以连接一个槽函数;

connect(myObjectA, &ObjectA::signalA, myReceiverD, &ObjectD::onSlotD);
connect(myObjectB, &ObjectB:signalB, myReceiverD, &ObjectD::onSlotD);
connect(myObjectC, &ObjectC::signalC, myReceiverD, &ObjectD::onSlotD);

4、如果一个信号连接多个信号/槽函数,那么绑定的信号/槽函数被触发的顺序与建立连接的顺序一致;

connect(myObjectA, &ObjectA::signalA, myReceiverB, &ObjectB::onSlotB);
connect(myObjectA, &ObjectA::signalA, myReceiverC, &ObjectC::onSlotC);
connect(myObjectA, &ObjectA::signalA, myReceiverD, &ObjectD::onSlotD);

emit myObjectA->signalA();
// 发送信号signalA,槽函数执行顺序onSlotB() -> onSlotC() -> onSlotD();

5、正常情况下,如果一个连接被调用两次(多次),则该信号对应的信号/槽函数会被触发两次(多次);

void ObjectB::onSlotB()
{
    qDebug() << "Run slotB";
}

connect(myObjectA, &ObjectA::signalA, myReceiverB, &ObjectB::onSlotB);
connect(myObjectA, &ObjectA::signalA, myReceiverB, &ObjectB::onSlotB);
connect(myObjectA, &ObjectA::signalA, myReceiverB, &ObjectB::onSlotB);

emit myObjectA->signalA();

// 调用三次输出三次;
Run slotB
Run slotB
Run slotB

6、特殊情况下,如果connect方法中的参数Qt::ConnectionType type = Qt::UniqueConnection,那么表示此次的连接是唯一的,建立过连接之后,再次调用同样的代码建立连接将以失败而告终,也就是只能connect成功一次,后面的connect将失效。

以下代码connect调用了四次,但是失败两次,因为connect中有三次参数 ConnectionType类型为Qt::UniqueConnection,所以只成功了一次,故信号触发,槽函数会被执行两次。

void ObjectB::onSlotB()
{
    qDebug() << "Run slotB";
}

// isSucces = true; (第一次调用,所以返回true)
isSucces = connect(myObjectA, &ObjectA::signalA, myReceiverB, &ObjectB::onSlotB, Qt::UniqueConnection);
// isSucces = false; (第二次调用,且ConnectionType 仍然为 Qt::UniqueConnection)
isSucces = connect(myObjectA, &ObjectA::signalA, myReceiverB, &ObjectB::onSlotB, Qt::UniqueConnection);
// isSucces = false; (第三次调用,且ConnectionType 仍然为 Qt::UniqueConnection)
isSucces = connect(myObjectA, &ObjectA::signalA, myReceiverB, &ObjectB::onSlotB, Qt::UniqueConnection);

// isSucces = true;(第四次调用,但是ConnectionType 默认为 AutoConnection)
isSucces = connect(myObjectA, &ObjectA::signalA, myReceiverB, &ObjectB::onSlotB);

emit myObjectA->signalA();

// 调用四次输出两次;
Run slotB
Run slotB

需要注意的是Qt::UniqueConnection对lambdas表达式或者非成员函数/仿函数不生效。

7、不管对象A的信号signalA()和对象的槽函数onSlotB()的信号槽连接被调用了一次或者多次(信号触发会一次或多次调用槽函数),只要调用了对应的disconnect方法,那么不管调用的多少次的connect方法,所有的连接都会断开。

void ObjectB::onSlotB()
{
    qDebug() << "Run slotB";
}

connect(myObjectA, &ObjectA::signalA, myReceiverB, &ObjectB::onSlotB);
connect(myObjectA, &ObjectA::signalA, myReceiverB, &ObjectB::onSlotB);
connect(myObjectA, &ObjectA::signalA, myReceiverB, &ObjectB::onSlotB);

disconnect(myObjectA, &ObjectA::signalA, myReceiverB, &ObjectB::onSlotB);

emit myObjectA->signalA();

// 调用三次无输出,因为调用了disconnect,所有的连接都断开;

8、信号槽中信号发送方对象sender或者接收方对象receiver其中一个被销毁connect将会自动断开

void ObjectB::onSlotB()
{
    qDebug() << "Run slotB";
}

myObjectA = new ObjectA;
myReceiverB = new ObjectB;

connect(myObjectA, &ObjectA::signalA, myReceiverB, &ObjectB::onSlotB);

disconnect(myObjectA, &ObjectA::signalA, myReceiverB, &ObjectB::onSlotB);

delete myReceiverB;

emit myObjectA->signalA();

// 无输出,因为对象myReceiverB被销毁;

9、there is an implicit conversion between the types of the corresponding arguments in the signal and the slot
信号和槽中相应参数的类型之间存在隐式转换

class MyWgtA : public QWidget
{
	Q_OBJECT

public:
	MyWgtA(QWidget *parent = nullptr) {};

signals:
	void signalA(double);
};

class MyWgtB : public QWidget
{
	Q_OBJECT

public:
	MyWgtB(QWidget *parent = nullptr) {};

	// 没有明确声明槽函数;
public:
	void onSlotB(int param)
	{
	    qDebug() << param;
	}
};

void test()
{
    MyWgtA* wgtA = new MyWgtA;
    MyWgtB* wgtB = new MyWgtB;
    connect(wgtA, &MyWgtA::signalA, wgtB, &MyWgtB::onSlotB);
    
    emit wgtA->signalA(12.8);
    
    // 输出12;
}

10、关于信号和槽函数自动绑定的问题,对应的槽函数必须写成on_控件名_信号名的格式,这种情况下Qt提供的元对象系统会提供一个机制自动关联相关的信号和槽函数。

下方是Qt源码中给出的解释;

Qt’s meta-object system provides a mechanism to automatically connect
signals and slots between QObject subclasses and their children. As long
as objects are defined with suitable object names, and slots follow a
simple naming convention, this connection can be performed at run-time
by the QMetaObject::connectSlotsByName() function.

如果出现这种情况,我们仍然再次使用connect去绑定这样的信号与槽函数,会输出以下警告:

QMetaObject::connectSlotsByName: No matching signal for slotName

// 待验证;
这里其实跟setupUi有关系,即带ui文件的界面类,因为designer中我们可以去绑定一个信号和槽函数,我们可以去看下ui文件生成的代码。

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

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

相关文章

用于语义图像分割的弱监督和半监督学习:弱监督期望最大化方法

这时一篇2015年的论文&#xff0c;但是他却是最早提出在语义分割中使用弱监督和半监督的方法&#xff0c;SAM的火爆证明了弱监督和半监督的学习方法也可以用在分割上。 这篇论文只有图像级标签或边界框标签作为弱/半监督学习的输入。使用期望最大化(EM)方法&#xff0c;用于弱…

rain-nowcasting-using-deep-learning github:使用深度学习进行临近降水预报

来源 github地址 是什么 本资料库旨在阐述 "在应用于降雨预报的深度学习模型中合并雷达雨量图像和风速预测 "&#xff08; “Merging radar rain images and wind predictions in a deep learning model applied to rain nowcasting”&#xff09;一文中提出的深度…

下拉表格多选sql批量插入以身份证号自动加载年龄性别生日

目录 一、layui下拉表格多选1、引入js2、html3、js代码①非动态数据②动态数据 4、运行效果 二、sql server批量插入三、根据身份证号动态填写出生日期年龄性别 一、layui下拉表格多选 1、引入js 2、html <div class"layui-input-inline"><input type&quo…

安装 Grafana 及 windows_exporter 配置 dubbo 配置 及报告示例

目录 安装部署 官网下载 配置中文 启动 访问 账户密码 界面效果 图表操作 新建添加仪表 添加 Prometheus 数据源 导入已有报告示例 下载 windows_exporter Grafana 的图表模板 node_exporter 中文版&#xff1a;windows_exporter for Prometheus Dashboard CN v2…

element框架select值更新页面不回显的问题,动态表单props绑定问题

1、页面中使用form表单&#xff0c;引入select组件 当data中默认没有定义form.region的值时&#xff0c;会出现选择select后input没有回显选择数据值&#xff1b;所以使用select时&#xff0c;必须定义默认值 <el-form ref"form" :model"form" label-…

常见面试题之线程中并发锁(二)

1. 什么是AQS&#xff1f; 1.1. 概述 全称是AbstractQueuedSynchronizer&#xff0c;是阻塞式锁和相关的同步器工具的框架&#xff0c;它是构建锁或者其他同步组件的基础框架 AQS与Synchronized的区别 synchronizedAQS关键字&#xff0c;c语言实现java语言实现悲观锁&#…

CISSP证书考试难度大吗?本文教你如何轻松拿下CISSP

主题&#xff1a;CISSP含金量、CISSP考试经验、CISSP备考、CISSP考试大纲 CISSP含金量高 CISSP注册信息系统安全师认证是信息安全领域被全球广泛认可的IT安全认证&#xff0c;一直以来被誉为业界的“金牌标准”。CISSP认证不仅是对个人信息安全专业知识的客观评估&#xff0c…

给若依添加单元测试(一)

给若依添加单元测试 方案二&#xff08;异常困难但企业开发一般用这个&#xff09; 方案一&#xff08;简单&#xff09; 在 admin 模块中添加单元测试 S1.在 src 目录下创建 test.java.MapperTests 文件 S2.将以下内容复制进去 import com.ruoyi.RuoYiApplication; imp…

初学mybatis(六)动态sql

学习回顾&#xff1a;初学mybatis&#xff08;五&#xff09; 一、动态SQL 介绍 什么是动态SQL&#xff1a;动态SQL指的是根据不同的查询条件 , 生成不同的Sql语句. 官网描述&#xff1a; MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其它类似框架的经验&…

小研究 - Java 指针分析综述(三)

近年来静态程序分析已成为保障软件可靠性、安全性和高效性的关键技术之一. 指针分析作为基 础程序分析技术为静态程序分析提供关于程序的一系列基础信息&#xff0c;例如程序任意变量的指向关系、变量 间的别名关系、程序调用图、堆对象的可达性等. 介绍了 Java 指针分析的重要…

微信小程序:实名认证登录 [2018年]

1、[微信开发社区]微信支付实名信息小程序授权接口能力&#xff08;用户获取用户真实姓名&#xff0c;身份证号码&#xff09; 微信开放社区 2、注意事项 四、接口文档 略。

English Learning - L3 纠音 W8 Lesson7 Ted Living Beyond Limits 2023.06.27 周二

朗读内容&#xff1a; Lesson 7 Day 47 - 51 句子 Ted Living Beyond Limits 3-22

GPT-4 参加2023年高考数学,人工智能对话机器人和人类对决,快谁速度快

ChatGPT从入门到精通&#xff0c;一站式掌握办公自动化/爬虫/数据分析和可视化图表制作 全面AI时代就在转角 道路已经铺好了 “局外人”or“先行者” 就在此刻 等你决定 让ChatGPT帮你高效实现职场办公&#xff01;行动起来吧。 【点击查看更多知识】ChatGPT从入门到精通&am…

springboot中Thymeleaf模板插入Freemarker模板页面

概述 最近在修改springboot架构的项目&#xff0c;项目之前配置的是Thymeleaf模板&#xff0c; 但是在我新加的功能中&#xff0c;我非常想用Freemarker模板来新加一些页面功能。 看到网上很多其他地方描述&#xff0c;要么用不同的文件后缀来区分(如html文件为Thymeleaf&…

【java 程序设计实训】学生请假管理系统

学生请假管理系统 运行结果学生请假管理系统需求分析GUI 编程事件处理数据库编程部分代码项目结构实体类 Admin.javaLeaveData.javaUserLogin.javaMainWindow.javaTeacherReviewFrame.javaleaveList.java 注&#xff1a;完整内容可下载查看完整报告 运行结果 学生请假管理系统需…

mac配置VScode主题加makefile etc

profile配置&#xff1a; 参考链接&#xff1a;https://www.bilibili.com/video/BV1YW4y1M7uX/?spm_id_from333.999.0.0&vd_sourced75fca5b05d8be06d13cfffd2f4f7ab5 https://code.visualstudio.com/docs/cpp/config-clang-mac vscode profiles如下&#xff1a; {//…

蓝桥杯专题-试题版-【九宫重排】【格子刷油漆】【回文数字】【国王的烦恼】

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列点击跳转>蓝桥系列 &#x1f449;关于作者 专注于Android/Unity和各种游…

空间曲线的切线和法平面与曲面的切平面和法线

&#xff08;一&#xff09;空间曲线的切线和法平面 1. 参数方程的形式 理解和记住如下逻辑&#xff1a; 该两个公式&#xff0c;笔者可以理解但是无法证明。 2. 参数方程外的第二种形式&#xff1a; 此种变换的本质&#xff0c;就是将多元函数转换为参数方程的形式。如此看…

English Learning - L3 作业打卡 Lesson7 Day54 2023.6.29 周四

English Learning - L3 作业打卡 Lesson7 Day54 2023.6.29 周四 引言&#x1f349;句1: I daydreamed like I did as a little girl and I imagined myself walking gracefully, helping other people through my journey and snowboarding again.成分划分连读爆破语调 &#x…

Learn Mongodb 可是工具及基本命令的使用 ③

作者 : SYFStrive 博客首页 : HomePage &#x1f4dc;&#xff1a; PHP MYSQL &#x1f4cc;&#xff1a;个人社区&#xff08;欢迎大佬们加入&#xff09; &#x1f449;&#xff1a;社区链接&#x1f517; &#x1f4cc;&#xff1a;觉得文章不错可以点点关注 &#x1f44…