【QT】元对象系统学习笔记(一)

news2025/4/27 16:05:47

QT元对象系统

      • 01、元对象系统
        • 1.1、 元对象运行原则
        • 1.2、 Q_OBJECT宏
        • 1.3、 Qt Creator启动元对象系统
        • 1.4、 命令行启动元对象(不常用)
      • 02、反射机制
        • 2.1、 Qt实现反射机制
        • 2.2、 反射机制获取类中成员函数的信息
          • 2.1.1、 QMetaMethon类
          • 2.1.2、QMetaObject类
        • 2.3、 反射机制获取类相关的信息

01、元对象系统

元对象系统是Qt对原有的C++进行的一些扩展,主要是为了实现信号和槽机制而引入的,信号和槽机制是Qt的核心特性。

Qt的元对象系统提供的功能有:
1、对象间通信的信号和槽机制
2、运行时类型信息和动态属性系统等

要使用元对象系统的功能,需要满足三个前置条件。

  1. 该类必须继承自QObject类。
  2. 必须在类申明的私有区域添加Q_OBJECT宏,该宏用于启动元对象特性,然后就可以使用动态特性、信号和槽等功能。
  3. 元对象编译器(moc)为每个QObject的子类提供实现了元对象特性所必须得代码。

1.1、 元对象运行原则

  • 因为元对象系统是对C++的扩展,因此使用传统的编译器是不能直接编译启用了元对象系统的Qt程序的,对此在编译Qt程序之前,需要把扩展的语法去掉,该功能就是moc要做的事情。
  • moc的全称是:Meta-Object Compiler(元对象编译器),它是一个工具(类似于qmake),该工具读取并分析C++源文件,若发现一个或多个包含了Q_OBJECT宏的类的申明,则会生成另外一个包含了Q_OBJECT宏实现代码的C++源文件(该源文件通常名称为:moc_*.cpp),这个新的源文件要么被#include包含到类 的源文件中,要么被编译键解到类的实现中(通常使用这种方法)。注意:新文件不会“替换”掉旧的文件,而是同源文件一起编译。

其他概念 :

1、元对象代码:指的是moc工具生成的源文件的代码,其中包含有Q_OBJECT宏的实现代码
2、moc工具的路径一般在安装路径下,如:C:\app\Qt5.8.0MinGw\5.8\mingw53_32\bin

1.2、 Q_OBJECT宏

先看一张图片,只要包含了Q_OBJECT宏,然后F2,就能进入。
在这里插入图片描述
可见,Q_OBJECT宏为申明的类之中增加了一些成员,而且红框标注的是虚函数成员(注意这些虚函数没有定义),按照C++语法,虚函数必须定义或者被申明为纯虚函数,moc工具的工作之一就是生成以上成员的定义,并且还会生成一些其他必要的代码。

1.3、 Qt Creator启动元对象系统

由于moc工具是通过Qt Creator来使用的,因此必须保证moc能发现并处理项目中包含有Q_OBJECT宏的类,所以,需要遵守以下规则:
1、从QObject派生的含有Q_OBJECT宏的类的定义必须在头文件中;
2、确保pro文件中,是否列举了项目中的所有源文件(SOURCES变量)和头文件(HEADERS变量);
3、应在头文件中使用逻辑指令如(#ifndef、#define、#endif)防止头文件重复包含多次。
4、QObject类应是基类列表中的第一个类。

由以上规则可见,使用 Qt Creator 编写代码时,类应定义在头文件中,成员函数
的定义应位于源文件中(这样可避免头文件被包含多次产生的重定义错误),虽然
这样编写程序比较麻烦,但这是一种良好的代码组织方式。

注意:如果定义了QObject类的派生类,并进行了构建(构建未添加O_OBJECT,而是在构建完成之后才添加),则此时必须再执行一次qmake命令,否则moc不能生成代码。

这里贴上书中的原话:
在这里插入图片描述
运行以上程序,可在 debug 目录下找到一个 moc_m.cpp 的源文件,该源文件就是使用 moc
工具生成的,该源文件中的代码就是元对象代码,读者可查看其代码。若在该目录没有
moc_m.cpp 文件,说明 moc 工具未能正常启动,这时需在 Qt Creator 中执行 qmake 命令,
再构建程序。

1.4、 命令行启动元对象(不常用)

  1. 在命令行需使用 moc 工具,并在源文件中包含 moc 工具生成的 cpp 文件。
  2. 此时包含 Q_OBJECT 的类不需要位于头文件中,假设位于 m.cpp 文件内,内容为:
#include<QObject>
class A : public QObject
{  
	Q_OBJECT
public:
  A(){}  
};

int main(int argc, char *argv[]) {
 	A ma; return 0; 
}
  1. 打开 Qt 5.8 for Desktop (MinGW 5.3.0 32 bit)命令行工具,输入如下命令:
moc d:\qt\m.cpp -o d:\qt\mm.cpp

以上命令会根据 m.cpp 生成 mm.cpp 文件,mm.cpp 文件中的代码就是元对象代码,此处
m.cpp 和 mm.cpp 都位于 d:\qt 文件夹下。

  1. 然后再次打开 m.cpp,在其中使用#include 把 mm.cpp 包含进去,如下:
#include <QObject>
//#include "mm.cpp"   // 不能把mm.cpp包含在类A的定义之前
class A : public QObject {
	Q_OBJECT
public:
	A(){}
};

#include "mm.cpp"  // 必须把mm.cpp包含在类A的定义后面,因为mm.cpp源文件中有对A的成员定义,此时必须见到类A的完整定义。

int main(int argc, char* argv[]) {
	A ma;
	return 0;
}
  1. 然后再使用 qmake 生成项目文件和 makefile 文件,再使用 mingw32-make 命令即可。

命令行这种方式,一般来说QT开发者是很少用到,因为Qt Creator已经帮我们做了这件事,这里只是看见书上有讲,记录一下。

02、反射机制

reflection模式(反射模式/反射机制):是指在运行时,能获取任意一个类对象的所有类型信息、属性、成员函数等信息的一种机制。

元对象系统与反射机制:
1、元对象系统提供的功能之一是为 QObject 派生类对象提供运行时的类型信息及数据成
员的当前值等信息,也就是说,在运行阶段,程序可以获取 QObject 派生类对象所属
类的名称、父类名称、该对象的成员函数、枚举类型、数据成员等信息,其实这就是
反射机制。

2、因为 Qt 的元对象系统必须从 QObject 继承,又从反射机制的主要作用可看到,Qt 的
元对象系统主要是为程序提供了 QObject 类对象及其派生类对象的信息,也就是说不
是从 QObject 派生的类对象,则无法使用 Qt 的元对象系统来获取这些信息。

元对象的具体解释:是指用于描述另一个对象结构的对象。

具象化到程序上如下:

class B
{
	//TODO....
};

class A : public B
{
	B mb;
};

假设mb是用来描述类A创建的对象的,则mb就是元对象。

2.1、 Qt实现反射机制

  1. Qt使用了一系列的类来实现反射机制,这些类对对象的各个反面进行了描述,其中QMetaObject类描述了QObject及其派生类对象的所有元信息,该类是Qt元对象系统的核心类,通过该类的成员函数可以获取QObject及其派生类对象的所有元信息,因此可以说QMetaObject类的对象是Qt中的元对象。 注意:要调用QMetaObject类中的成员函数需要使用QMetaObject类型的对象。
  2. 对对象的成员进行描述:一个对象包含数据成员、函数成员、构造函数、枚举成员等,在Qt中,这些成员分别使用了不同的类对其进行描述。比如函数成员使用类QMetaMethod、属性使用QMetaProperty进行描述等。最后使用QMetaObject类对整个类对象进行描述,比如要获取成员函数的变量名:
QMetaMethod qm = metaObject->method(1);  // 获取索引为1的成员函数
qDebug()<< qm.name() << "\n";  // 输出该成员函数的名称

使用Qt反射机制的前置条件:

  1. 需要继承自QObject类,并需要在类之中加入Q_OBJECT宏。
  2. 注册成员函数:若希望普通成员函数能够被反射,需要在函数声明之前加入QObject::Q_INVOKABLE宏。
  3. 注册成员变量:若希望成员变量能够被反射,需要使用Q_PROPERTY宏。

其实关于QObject::Q_INVOKABLEQ_PROPERTY这两个宏,在Qt5中的QQuick中就有很深切的体会,QML要使用C++这边的属性、方法都是需要注明这个宏的。

Qt 反射机制实现原理简述:

  1. Q_OBJECT宏展开之后有一个虚拟成员函数meteObject(),该函数会返回一个指向QMetaObject类型的指针,其原型为:
virtual const QMetaObject* metaObject() const;

因为启动了元对象系统的类都包含了Q_OBJECT宏,所以这些类都含有metaObject()虚拟成员函数,通过该函数返回的指针调用QMetaObject类中的成员函数,便可以查询到QObject及其派生类对象的各种信息。

  1. Qt的moc会完成以下工作:
    1、为Q_OBJECT宏展开后所声明的成员函数的生成实现代码;
    2、识别Qt中特殊的关键字及宏,比如识别出Q_PROPRTYQ_INVOKABLEslotsignals等。

2.2、 反射机制获取类中成员函数的信息

2.1.1、 QMetaMethon类

作用:用于描述对象的成员函数,可使用该类的成员函数获取对象成员函数的信息。

列举一些常用的成员:

// 此枚举用于描述函数的类型,即:普通成员函数、信号、槽、构造函数
enum MethodType {Method, Signal, Slot, Constructor}

// 此枚举主要用于描述函数的访问级别(私有的、受保护的、公有的)
enum Access {Private,Protected,Public}

// 返回函数的签名(qt5.0)
QByteArray methodSignature() const;

// 返回函数的类型(信号、槽、成员函数、构造函数)
MethodType methodType() const;

// 返回函数的名称(qt5.0)
QByteArray name() const;

// 返回函数的参数数量(qt5.0)
int parameterCount() const;

// 返回函数参数名称的列表
QList<QByteArray> parameterNames() const;

// 返回指定索引处的参数类型,返回值是使用QMetaType注册的类型,若类型未注册,则返回值为QMetaType::UnknownType。
int parameterType(int index) const;

// 返回函数参数类型的列表
QList<QByteArray> parameterTypes() const;

// 返回函数的返回类型。返回值是使用QMetaType注册的类型,若类型未注册,则返回值为QMetaType::UnknownType。
int returnType() const;

// 返回函数的返回类型的名称
const char* typeName() const;

// 返回函数的访问级别(私有的、受保护的、公有的)
Access access() const;
2.1.2、QMetaObject类

作用:用于提供关于类的元对象信息。

列举一些常用的成员:

/* 返回名为 f 的函数的索引号,否则返回-1。此处应输入正确的函数签名,比如函数形
式为 void f(int i,int j);则正确的形式为 xx.indexOfMethod("f(int,int"); 以下形式都不是
正确的形式,"f(int i, int j)"、"void f(int, int)"、 "f"、"void f"等。*/
int indexOfMethod(const char* f) const;

// 返回信号 s 的索引号,否则返回-1,若指定的函数存在,但不是信号,仍返回-1。
int indexOfSignal(const char * s) const;

// 返回构造函数 c 的索引号,否则返回-1
int indexOfConstructor(const char *c) const;

// 返回构造函数的数量。
int constructorCount() const ; 

// 返回指定索引 i 处的构造函数的元数据。
QMetaMethod constructor(int i)const;

// 返回函数的数量,包括基类中的函数、信号、槽和普通成员函数。
int methodCount() const;

// 返回父类中的所有函数的总和,也就是说返回的值是该类中的第一个成员函数的索引位置。
int methodOffset() const;

// 返回指定索引 i 处的函数的元数据。
QMetaMethod method(int i) const;

下面是书上的一个示例,仅供参考

#include "m.h"
#include <QMetaMethod>
#include <QByteArray>
#include <iostream>
using namespace std;
int main(){  A ma; B mb; //创建两个对象
const QMetaObject *pa=ma. metaObject ();
const QMetaObject *pb=mb. metaObject ();
//以下为通过 QMetaObject 的成员函数获取的信息。
int j=pa->methodCount(); /*返回对象 ma 中的成员函数数量,包括从父类 QObject 继承而来的 5 个
成员函数及本对象中的 2 个成员函数(注意,不包括 g1)、1 个信号,所以
总数为 8。*/
cout<<j<<endl; //输出 8
int i=pa->indexOfMethod("g(int,float)"); //获取对象 ma 中的成员函数 g 的索引号。
cout<<i<<endl; //输出 7
i=pa->constructorCount(); //获取对象 ma 所属类中的构造函数的数量。
cout<<i<<endl; //输出 2
i=pb->constructorCount(); /*获取对象 mb 所属类 B 中的构造函数的数量,因类 B 无构造函数,所以
返回值为 0,此处也可看到,构造函数数量不包含父类的构造函数*/
cout<<i<<endl; //输出 0。
i=pa->indexOfConstructor("A(int)"); //获取对象 ma 所属类中的构造函数 A(int)的索引号。
cout<<i<<endl; //输出 1。
i=pa->indexOfSignal("gb3()"); //获取对象 ma 的信号 gb3()的索引号。
cout<<i<<endl; //输出 5。
i=pa->indexOfSignal("f()"); /*获取对象 ma 的信号 f()的索引号。因为成员函数 f 存在,但不是信
号,所以返回值为-1。*/
cout<<i<<endl; //输出-1。
i=pb->methodOffset(); /*获取父类的成员函数数量,包括父类A及QObject中的成员函数,总共为8。
*/
cout<<i<<endl; //输出 8,此处相当于是对象 mb 自身成员函数开始处的索引号。
//以下为通过 QMetaMethon 的成员函数获取的信息。
//获取对象 ma 的成员函数 g 的元数据。
QMetaMethod m=pa->method(pa->indexOfMethod("g(int,float)"));
QByteArray s= m.name(); //获取成员函数 g 的函数名。
cout<<s.data()<<endl; //输出 g
s=m.methodSignature(); //获取函数 g 的签名
cout<<s.data()<<endl; //输出 g(int,float)
i=m.methodType(); /*获取函数 g 的类型,此处返回的是 QMetaMethod::MethodType 中定义的枚举值,
其中 Method=0,表示类型为成员函数*/
cout<<i<<endl; //输出 0(表示成员函数)。
//以下信息与函数的返回类型有关
s=m.typeName(); //获取函数 g 的返回值的类型名
cout<<s.data()<<endl; //输出 void
i=m.returnType(); /*获取函数 g 返回值的类型,此处的类型是 QMetaType 中定义的枚举值,其中枚举
值 QMetaType::void=43*/
cout<<i<<endl; //输出 43
//以下信息与函数的参数有关
i=m.parameterType(1); /*获取函数 g 中索引号为 1 的参数类型,此处的类型是 QMetaType 中定义的
枚举值,其中枚举值 QMetaType::float=38*/
cout<<i<<endl; //输出 38
QList<QByteArray> q=m.parameterNames(); //获取函数 g 的参数名列表
cout<<q[0].data()<<q[1].data()<<endl; //输出 ij
q=m.parameterTypes(); //获取函数 g 的参数类型列表。
cout<<q[0].data()<<q[1].data()<<endl; //输出 intfloat
return 0; }

2.3、 反射机制获取类相关的信息

  1. QMetaObject类中获取与类相关的信息的成员函数有:
/*获取类的名称,注意,若某个 QObject 的子类未启动元对象系统(即未使用 Q_OBJECT
宏),则该函数将获取与该类最接近的启动了元对象系统的父类的名称,而不再返回
该类的名称,因此建议所有的 QObject 子类都使用 Q_OBJECT 宏。
*/
const char* className() const;

// 返回父类的元对象,若没有这样的对象则返回0
const QMetaObject* superClass() const;

// 若该类继承自mo描述的类型,则返回true,否则返回false。类被认为继承自身。
bool inherits(const QMetaObject* mo) const;  // (Qt5.7)
  1. QObject类中获取与类相关的信息的成员函数有:
// 若该类是className指定的类的子类则返回true,否则返回false。类被认为继承自身
bool inherits(const char* className) const;

下面是一个示例:

头文件:

#ifndef M_H //要使用元对象系统,需在头文件中定义类。
#define M_H
#include<QObject>
class A:public QObject{ Q_OBJECT};
class B:public A{ Q_OBJECT};
class C:public QObject{Q_OBJECT};
class D:public C{};
#endif // M_H

源文件:

#include "m.h"
#include <QMetaMethod>
#include <iostream>
using namespace std;
int main(){  A ma; B mb; C mc; D md;
const QMetaObject *pa=ma. metaObject ();
const QMetaObject *pb=mb. metaObject ();
cout<<pa->className()<<endl; //输出类名 A
//使用 QMetaObject::inherits()函数判断继承关系。
cout<<pa->inherits(pa)<<endl; //输出 1,类被认为是自身的子类
cout<<pa->inherits(pb)<<endl; //输出 0,由 pb 所描述的类 B 不是类 A 的子类。
cout<<pb->inherits(pa)<<endl; //输出 1,由 pa 所描述的类 A 是类 B 的子类。
//使用 QObject::inherits()函数判断继承关系。
cout<<ma.inherits("B")<<endl; //输出 0,类 A 不是类 B 的子类。
cout<<ma.inherits("A")<<endl; //输出 1,类被认为是自身的子类
cout<<md.inherits("D")<<endl; //输出 0,因为类 D 未启动元对象系统。
cout<<md.inherits("C")<<endl; /*输出 1,虽然类 D 未启动元对象系统,但类 C 已启动,此种情形下
能正确判断继承关系。*/
cout<<md. metaObject ()->className()<<endl; /*输出 C,此处未输出 D,因为类 D 未启动元对象系统,
与类 D 最接近的启动了元对象系统的父类是 C,因此返回 C。*/
return 0; }
  1. qobject_cast函数,使用语法如下:
DestType* qobject_cast<DestType*>(QObject* p);

1、该函数类似于 C++中的 dynamic_cast,但执行速度比 dynamic_cast 更快,且不需要
C++的 RTTI 的支持,但 qobject_cast 仅适用于 QObject 及其派生类。
2、主要作用是把源类型 QObject 转换为尖括号中的目标类型 DesType(或者子类型),并
返回指向目标类型的指针,若转换失败,则返回 0。或者说源类型 QObject 属于目标
类型 DestType(或其子类型),则返回指向目标类型的指针,否则返回 0。
3、使用 qobject_cast 的条件:目标类型 DestType 必须继承(直接或间接)自 QObject,并
使用 Q_OBJECT 宏。

一个示例:

头文件:

#ifndef M_H //要使用元对象系统,需在头文件中定义类。
#define M_H
#include<QObject>
#include <iostream>
using namespace std;
class A:public QObject{ Q_OBJECT
public:void fa(){cout<<"FA"<<endl;}  };
class B:public A{ Q_OBJECT
public:void fb(){cout<<"FB"<<endl;}  };
class C:public QObject{Q_OBJECT
public:void fc(){cout<<"FC"<<endl;}  };
class D:public C{ public: void fd(){cout<<"FD"<<endl;} };
#endif // M_H

源文件:

#include "m.h"
#include <QMetaMethod>
#include <iostream>
using namespace std;
//qobject_cast 的简单应用(类型判断)
void g(QObject *p){
if(qobject_cast<A*>(p)) //若 p 是类 A 及其派生类类型
{cout<<"GA"<<endl;}
if(qobject_cast<B*>(p))//若 p 是类 B 及其派生类类型
{cout<<"GB"<<endl;}
else //若 p 不是类 B 及其派生类类型
cout<<"XX"<<endl;  }
int main(){  A *pa=new A; B *pb=new B; C *pc=new C; D *pd=new D;
qobject_cast<B*>(pa)->fb(); //输出 FB,转换成功后可调用子类中的函数。
//qobject_cast<D*>(pc); //错误,因为类 D 未使用 Q_OBJECT 宏。
g(pa); //输出 GA、XX。因为 pa 不是 B 及其派生类类型所以会输出 XX。
g(pb); //输出 GA、GB。因为 pb 是 A 的派生类类型,所以首先输出 GA,然后输出 GB。
g(pc); //输出 XX,因为 pc 即不是 A 也不是 B 及其派生类的类型,所以输出 XX。
g(pd); //输出 XX,原因同上。
return 0; }

未完待续!!!

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

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

相关文章

【程序员必须掌握哪些算法?】

一个程序员一生中可能会邂逅各种各样的算法&#xff0c;但总有那么几种&#xff0c;是作为一个程序员一定会遇见且大概率需要掌握的算法。今天就来聊聊这些十分重要的“必抓&#xff01;”算法吧~ 常见算法介绍 本文所介绍的排序算法均以升序为例。 文章目录 常见算法介绍一 …

【解决】mysql卸载之后安装不同的版本导致mysqld无法启动

解决mysql不同版本之间数据的冲突 一. 背景二. 出现的问题三. 问题的原因四. 解决方式 一. 背景 说起来也是个巧合&#xff0c;在我安装mysql5.7版本的时候&#xff0c;看走眼了&#xff0c;安装成mysql8.0版本的了。于是乎&#xff0c;我当时觉得8.0&#xff0c;嗯&#xff0…

机械臂多任务逆运动学(优先级同等和存在优先级)

我们经常使用微分运动学来计算机器人的逆运动学&#xff0c;对于单个任务的机械臂的逆运动学使用的是梯度投影法&#xff1a; 冗余机械臂求解逆运动学解——梯度投影法 但是对于多任务的逆运动学在一般的机器人学里面很少有提及&#xff0c;最近看到了相关的论述&#xff0c;…

IP基础知识总结

IP他负责的是把IP数据包在不同网络间传送&#xff0c;这是网络设计相关的&#xff0c;与操作系统没有关系。所以这部分知识&#xff0c;不是网络的重点。IP和路由交换技术联系紧密。但是要作为基本知识点记住。 一、基本概念 网络层作用&#xff1a;实现主机与主机之间通信。 …

MySQL数据库操作篇4(内置函数连接查询子查询)

MySQL除了提供一些聚合函数供我们使用&#xff0c;同时还提供了很多的内置扩展函数&#xff0c;这些函数有的是进行日期处理的&#xff0c;有的是进行字符串处理的&#xff0c;有的则是进行数值处理&#xff0c;以及其它的种种函数&#xff0c;这些函数可以帮助我们对数据进行加…

创建/查看/编辑文本文件

创建/查看/编辑文本文件 将输出重定向到文件和程序 标准 输入/输出/错误 运行的程序需要从某个位置读取输入并将输出写入某位置。从shell提示符运行的命令通常会从键盘读取输入&#xff0c;并将输出发送到其终端窗口。 进程通过使用文件描述符的通道编号获取输出并发送输出。…

红队打靶:billu_b0x打靶思路详解(vulnhub)

目录 写在开头 第一步&#xff1a;主机发现和端口扫描 第二步&#xff1a;Web渗透 第三步&#xff1a;利用文件包含进行代码审计 第四步&#xff1a;图片马上传与反弹shell 第五步&#xff1a;敏感文件提权 总结与思考 写在开头 我的博客等级终于到三级了&#xff0c;…

Python接口自动化搭建过程,含request请求封装

开篇碎碎念 接口测试自动化好处 显而易见的好处就是解放双手&#x1f600;。 可以在短时间内自动执行大量的测试用例通过参数化和数据驱动的方式进行测试数据的变化&#xff0c;提高测试覆盖范围快速反馈测试执行结果和报告支持持续集成和持续交付的流程 使用Requestspytes…

Attention Is All Your Need

q,k,v是一个东西(自注意力) 由于位置编码通过sin和cos得到,在+-1之间抖动,因此为了与embedding相加scale匹配,所以embedding除以根号dk 注意力机制:注意力函数 quary(不同的q) 和 k 的相似度决定了value对应的权重(不同的权重)相加得到 (不同的)输出 英文积累…

宠物赛道又出爆品!TikTok播放破千万!

在欧美国家&#xff0c;养宠的风潮已经持续百年。美国作为全国第一大的宠物市场&#xff0c;其家庭的养宠率高达70%。有关数据显示&#xff0c;美国目前26-41岁人群比例占比最大&#xff0c;已达到32%&#xff0c;42-57岁人群比例下降到24%。 养宠人群逐渐呈现年轻化&#xff0…

参数名的映射,小心使用strict=False

从vgg16-397923af.pth里读取的数值应该和加载预训练模型后model.load_state_dict参数一致。 而我的不一致&#xff01; 原因&#xff1a;在载入参数到模型键值的不匹配&#xff0c;所以使用了strictFalse。 解决办法&#xff1a; 进行参数名的映射&#xff0c;将不匹配的参数名…

不看不知,道,一键ai绘画软件也能生成这么好看的图片

结构&#xff1a; 前阵子&#xff0c;我朋友发了几张男生的照片在朋友圈&#xff0c;我还以为是她脱单了&#xff0c;赶紧给她送上了祝福。 她收到我的祝福后&#xff0c;笑嘻嘻地跟我解释说&#xff1a;“那几张图是我用AI绘画软件生成的&#xff0c;是不是很看起来很真呀&a…

一款剧情特别优秀的ARPG 游戏《FC魔神英雄传》

文章目录 介绍游戏发行游戏玩法 游戏剧情详细介绍游戏开始阶段剧情任务汇总&#xff1a;草原阶段冰川阶段海边阶段丛林阶段沙漠阶段湖泊阶段草原2阶段海边2阶段冰川2阶段山脉阶段 世界地图汇总 道具系统装备系统战斗系统战斗规则魔法技能魔法学习方式 游戏优点游戏缺点该游戏值…

接口测试 使用 rest-assured 进行接口测试

目录 前言 正文 1.HTTP 状态码 2.节点值验证 3.格式化打印 json 4.参数化 5.响应时间 6.JSON Schema Validation 前言 使用Rest-Assured进行接口测试是一个很好的选择。Rest-Assured是一个基于Java的开源库&#xff0c;专门用于简化和增强RESTful接口的测试。它提供了…

我在VScode学Java类与对象(Java的类与对象、this关键字)第一辑

我的个人博客主页&#xff1a;如果’真能转义1️⃣说1️⃣的博客主页 关于Java基本语法学习---->可以参考我的这篇博客&#xff1a;《我在VScode学Java》 关于我在VScode学Java&#xff08;Java方法method&#xff09; 类是描述了一组有相同特性&#xff08;属性&#xff09…

python_selenuim获取csdn新星赛道选手所在城市用echarts地图显示

文章目录 ⭐前言⭐selenuim获取新星赛道选手主页&#x1f496; 获取参赛选手主页思路分析&#x1f496; 获取参赛选手ip属地思路分析&#x1f496; echarts可视化展示 ⭐结束 ⭐前言 大家好&#xff0c;我是yma16&#xff0c;本文分享python_selenuim获取csdn新星赛道选手所在…

Requests —— Requests模块获取响应内容

Requests模块获取响应内容 响应包括响应行、响应头、响应正文内容&#xff0c;这些返回的响应信息都可以通过Requests模块获取。这些 获取到的响应内容也是接口测试执行得到的实际结果。 获取响应行 获取响应头 获取其它响应信息 代码示例&#xff1a; # 导入requests模块…

特征工程和多项式回归

特征工程的定义 特征工程&#xff08;Feature Engineering&#xff09;特征工程是将原始数据转化成更好的表达问题本质的特征的过程&#xff0c;使得将这些特征运用到预测模型中能提高对不可见数据的模型预测精度。 特征工程简单讲就是发现对因变量y有明显影响作用的特征&#…

【kubernetes系列】Kubernetes之配置dashboard安装使用

Kubernetes之配置dashboard 概述 Dashboard 是基于网页的 Kubernetes 用户界面。 你可以使用 Dashboard 将容器应用部署到 Kubernetes 集群中&#xff0c;也可以对容器应用排错&#xff0c;还能管理集群资源。 你可以使用 Dashboard 获取运行在集群中的应用的概览信息&#x…

MybatisPlus-2

springmybatismybatisplus 创建mapper接口&#xff0c;继承BaseMapper<obj>&#xff0c;obj为你需要操作的数据表创建对应的实体类配置数据源 4.设置配置文件 <?xml version"1.0" encoding"UTF-8"?> <beans xmlns"http://www.spri…