easyx图形库

news2025/2/24 9:53:23

目录

1、绘制简单的图形化窗口

2、设置窗口属性

2.1 颜色设置

2.2 刷新

3、基本绘图函数

3.1 绘制直线

3.2 绘制圆

3.3 绘制矩形

4、贴图

4.1 原样贴图

4.1.1 IMAGE变量去表示图片

4.1.2 加载图片 

4.1.3 显示图片

4.2 透明贴图

4.2.1 认识素材

4.3 png贴图

5、按键交互

5.1 小球自由移动

5.2 按键控制小球

6、鼠标交互

1、绘制简单的图形化窗口

在之前,我们一运行程序会显示出来的黑框框是控制台窗口,现在,我们要绘制的是另一个窗口,即图形化窗口

首先需要包含头文件,这里有两个头文件可以使用,任意选择一个即可

1. graphics.h : 包含已经被淘汰的的函数

2. easyx.h : 只包含最新的函数

创建图形化窗口只需要两个函数:

1. 打开图形化窗口:initgraph(int x, int y, int style)

2. 关闭图形化窗口:closegraph()

在initgraph中,x和y是要创建的图形化窗口的大小,第三个参数是创建方式,若传入1则表示创建图形化窗口的同时还要创建控制台窗口,若不传或者传入的不是1,则表示只创建图形化窗口

打开完图形化窗口后一定记得要关闭图形化窗口 

void test_graph()
{
	initgraph(800, 600);
	while (1); // 为了防止一闪而过
	closegraph();
}

图形化窗口的坐标:

2、设置窗口属性

2.1 颜色设置

setbkcolor(颜色)

其中这面的颜色有两种形式传入

1. 颜色宏:既使用颜色英语的大写,这是编译器定义好的,可以在编译器内查看

即可查看可以使用的颜色

2. RGB配置

在电脑自带的画图当中,就可以找到一种颜色对应的RGB

所以我们此时可以写出这样的代码

void test_graph()
{
	initgraph(800, 600);
	setbkcolor(RED);
	while (1); // 为了防止一闪而过
	closegraph();
}

 但运行起来后会发现,图形化窗口仍然是黑色的,并没有变成设置的红色,这是为什么呢?

因为我们没有刷新

2.2 刷新

当我们设置完图形化窗口的颜色后,还需要刷新才能看到我们设置的颜色,否则仍然是initgraph打开的图形化窗口默认的黑色

void test_graph()
{
	initgraph(800, 600);
	setbkcolor(RED);
	cleardevice();
	while (1); // 为了防止一闪而过
	closegraph();
}

这样图形化窗口就变成了红色

3、基本绘图函数

3.1 绘制直线

line(int x, int y, int xx, int yy)

第一、二个参数是起点的坐标,第三、四个参数是终点的坐标

可以使用setlinecolor(颜色)来改变线的颜色

3.2 绘制圆

circle(int x, int y, int r)

第一、二个参数是圆心的坐标,第三个参数是半径

使用circle画出的圆是带线的,带线是指带边框线,并且圆内部的颜色是和图形化窗口的背景颜色相同的,若想要填充圆的颜色,需要使用下面的函数

填充圆,设置填充颜色:setfillcolor(颜色)

带线:fillcircle(int x, int y, int r)

不带线:solidcircle(int x, int y, int r)

注意:要填充圆的颜色只能使用下面这两个函数来画圆,不能使用circle

3.3 绘制矩形

rectangle(int x, int y, int xx, int yy)

第一、二个参数是矩形左上角的点的坐标,第三、四个参数是矩形右下角的点的坐标

填充矩形,设置填充颜色:setfillcolor(颜色)

带线:fillrectangle(int x, int y, int xx, int yy)

不带线:solidrectangle(int x, int y, int xx, int yy)

这里的规则与绘制圆中的规则是类似的,就不过多赘述了

void test_graph()
{
	initgraph(800, 600);
	setbkcolor(RED);
	cleardevice();
	line(0, 0, 800, 600);
	setfillcolor(BLUE);
	fillcircle(100, 100, 50);
	solidrectangle(200, 200, 300, 300);
	while (1); // 为了防止一闪而过
	closegraph();
}

我们可以使用刚刚学习的知识来绘制一个棋盘

void draw()
{
	initgraph(400, 400);
	for (int i = 0; i <= 400; i += 20)
	{
		line(0, i, 400, i);
		line(i, 0, i, 400);
	}
	while (1);
	closegraph();
}

此时画出来的是黑底白线,也可以修饰一下

void draw()
{
	initgraph(400, 400);
	setbkcolor(BLUE);
	cleardevice();
	setlinecolor(BLACK);
	for (int i = 0; i <= 400; i += 20)
	{
		line(0, i, 400, i);
		line(i, 0, i, 400);
	}
	while (1);
	closegraph();
}

此时就变成了蓝底黑线了

4、贴图

4.1 原样贴图

原样贴图就是将一张图片直接原封不动的贴到图形化窗口中

要使用原样贴图有三个步骤:

4.1.1 IMAGE变量去表示图片

图片是一个IMAGE类型的变量,类似于1是一个int类型的变量,所以可以int a = 1,用a来存储1

所以需要先创建一个IMAGE变量来存储图片

4.1.2 加载图片 

使用函数loadimage(IMAGE* img, URL, int width, int height)

第一个参数是指向图片的指针,传参时传的就是图片的地址,第二个参数是图片所在的路径(可以用相对路径,也可以用绝对路径),第三、四个参数可传可不传,不传是就是将图片原本的大小贴到图形化窗口中,若传了则可以修改图片在图形化窗口上的大小

4.1.3 显示图片

putimage(int x, int y, IMAGE* img)

第一、二个参数是图片左上角的坐标,第三个参数是指向图片的指针

此时演示一下。通常,我们会在.cpp的路径下创建一个文件夹,然后将相关资源放到这个文件夹里面,这里我们就可以将图片放到这个文件夹里面

编译器还要修改一下属性,修改成“使用多字节字符集”

void test_maps()
{
	initgraph(800, 600);
	IMAGE img;
	loadimage(&img, "./Res/qiqi.jpg");
	putimage(0, 0, &img);
	while (1); // 为了防止一闪而过
	closegraph();
}

此时会发现,图片太大了,设置的这个图形化窗口太小了,无法完全显示,所以我们可以设置图片的大小。缩放是为了解决图片大小与窗口大小不一致

void test_maps()
{
	initgraph(800, 600);
	IMAGE img;
	loadimage(&img, "./Res/qiqi.jpg", 800, 600);
	putimage(0, 0, &img);
	while (1); // 为了防止一闪而过
	closegraph();
}

此时就可以将图片完全显示了

4.2 透明贴图

当我们需要将两张图片放到同一个图形化窗口中时,其中一张作为背景图,另一张只需要一部分,此时会发现只需要一部分的那一张图片的一部分会遮挡住背景图

让第一张作为背景图,第二种放在其中

void test_maps()
{
	initgraph(800, 600);
	IMAGE img;
	loadimage(&img, "./Res/qiqi.jpg", 800, 600);
	putimage(0, 0, &img);
	IMAGE hz;
	loadimage(&hz, "./Res/hz.jpg",100,100);
	putimage(100, 100, &hz);
	while (1); // 为了防止一闪而过
	closegraph();
}

 

此时第二张图片的背景图也会影响第一张图片,那要如何才能去除第二张图片的背景图呢?

4.2.1 认识素材

首先,我们需要将第二章图片分别做成掩码图和背景图,利用PS

掩码图:想要显示的部分弄成黑色,不想显示的部分弄成白色

背景图:想要显示的部分不动,不想显示的部分弄成黑色

弄好了掩码图和背景图后,按照特定步骤贴图即可

SRCAND  贴掩码图

SRCPAINT  贴背景图   

要先贴掩码图,再贴背景图

void test_maps()
{
	initgraph(800, 600);
	IMAGE img;
	loadimage(&img, "./Res/qiqi.jpg", 800, 600);
	putimage(0, 0, &img);
	IMAGE test[2];
	loadimage(test + 0, "./Res/ym.jpg", 100, 100);
	loadimage(test + 1, "./Res/bj.jpg", 100, 100);
	putimage(100, 100, test + 0, SRCAND);
	putimage(100, 100, test + 1, SRCPAINT);
	while (1); // 为了防止一闪而过
	closegraph();
}

此时运行结果就是正常的

4.3 png贴图

#pragma once
#include<easyx.h>
// 把像素的颜色拆解出来
typedef struct _ARGB
{
	byte a;
	byte r;
	byte g;
	byte b;
}ARGB;
// 颜色拆解
ARGB color2Argb(DWORD c)
{
	ARGB res;
	res.r = (byte)c;
	res.g = (byte)(c >> 8);
	res.b = (byte)(c >> 16);
	res.a = (byte)(c >> 24);
	return res;
}
DWORD argb2Color(ARGB c)
{
	DWORD t = RGB(c.r, c.g, c.b);
	return ((DWORD)c.a) << 24 | t;
}
// 把彩色图转成黑白图
void toGray(IMAGE* src)
{
	DWORD* psrc = GetImageBuffer(src);
	for (int i = 0; i < src->getwidth() * src->getheight(); i++)
	{
		// 获取每一个像素点的颜色值
		ARGB t = color2Argb(psrc[i]);
		// 灰度圈,求三个或者四个颜色值的均值
		byte arv = (t.r + t.g + t.b) / 3;
		ARGB res = { t.a,arv,arv,arv };
		psrc[i] = argb2Color(res);
	}
}
// @png透明贴图
void drawImg(int x, int y, IMAGE* src)
{
	// 变量初始化
	DWORD* pwin = GetImageBuffer();
	DWORD* psrc = GetImageBuffer(src);
	int win_w = getwidth();
	int win_h = getheight();
	int src_w = src->getwidth();
	int src_h = src->getheight();

	// 计算贴图的实际长度
	int real_w = (x + src_w > win_w) ? win_w - x : src_w;       // 处理超出右边界
	int real_h = (y + src_h > win_h) ? win_h - y : src_h;       // 处理超出下边界
	if (x < 0) { psrc += -x;           real_w -= -x; x = 0; }   // 处理超出左边界
	if (y < 0) { psrc += (src_w + -y); real_h -= -y; y = 0; }   // 处理超出上边界

	// 修正贴图起始位置
	pwin += (win_w * y + x);

	// 实现透明贴图
	for (int iy = 0; iy < real_h; iy++)
	{
		for (int ix = 0; ix < real_w; ix++)
		{
			byte a = (byte)(psrc[ix] >> 24);// 计算透明通道的值[0,256) 0为完全透明 255为完全不透明
			if (a > 100) pwin[ix] = psrc[ix];
		}
		// 换到下一行
		pwin += win_w;
		psrc += src_w;
	}
}

5、按键交互

案件交互分为阻塞按键交互和非阻塞按键交互

阻塞按键交互:不按按键时图片不会动

C语言中很多函数都是阻塞型的,如scanf,没有输入时程序就停止在哪里了

非阻塞按键交互:不按按键时图片可以动

接下来我们来实现两个案例

5.1 小球自由移动

struct Ball // 球的结构体
{
	int x;   // 球的横坐标
	int y;   // 球的纵坐标
	int r;   // 球的半径
	int dx;  // 球一次向x轴正方向移动的距离
	int dy;  // 球一次向y轴正方向移动的距离
};
struct Ball ball = { 400,400,15,5,-4 }; // 定义一个球
void DrawBall(struct Ball ball) // 画出球的函数
{
	setfillcolor(RED);
	solidcircle(ball.x, ball.y, ball.r);
}
void MoveBall() // 控制球移动的函数
{
	ball.x += ball.dx;
	ball.y += ball.dy;
}
int main()
{
	initgraph(800, 800);
	while (1)
	{
		DrawBall(ball); // 首先画出一个球
		MoveBall(); // 移动完成后更新了小球的状态,等待下一次进入循环后打印出新状态的小球
		Sleep(20); // 延时20ms
	}
	return 0;
}

但此时小球移动会有轨迹存在,所以在开始画出更新完状态之后的小球时,要先刷新

int main()
{
	initgraph(800, 800);
	while (1)
	{
		cleardevice(); // 刷新
		DrawBall(ball); // 首先画出一个球
		MoveBall(); // 移动完成后更新了小球的状态,等待下一次进入循环后打印出新状态的小球
		Sleep(20); // 延时20ms
	}
	return 0;
}

此时小球只会往一个方向移动,所以我们需要控制一下小球在碰撞到图形化窗口边界时,要反弹

void MoveBall() // 控制球移动的函数
{
	if (ball.x - ball.r <= 0 || ball.x + ball.r >= 800) { ball.dx = -ball.dx; }
	if (ball.y - ball.r <= 0 || ball.y + ball.r >= 800) { ball.dy = -ball.dy; }
	ball.x += ball.dx;
	ball.y += ball.dy;
}

5.2 按键控制小球

当用户按下按键时,也会产生一个值,可以用_getch()来获取,头文件是<conio.h>,然后根据这个值,来对小球做出处理

struct Ball // 球的结构体
{
	int x;   // 球的横坐标
	int y;   // 球的纵坐标
	int r;   // 球的半径
	int dx;  // 球一次向x轴正方向移动的距离
	int dy;  // 球一次向y轴正方向移动的距离
};
struct Ball ball = { 400,400,15,5,-4 }; // 定义一个球
void DrawBall(struct Ball ball) // 画出球的函数
{
	setfillcolor(RED);
	solidcircle(ball.x, ball.y, ball.r);
}
void MoveBall() // 控制球移动的函数
{
	if (ball.x - ball.r <= 0 || ball.x + ball.r >= 800) { ball.dx = -ball.dx; }
	if (ball.y - ball.r <= 0 || ball.y + ball.r >= 800) { ball.dy = -ball.dy; }
	ball.x += ball.dx;
	ball.y += ball.dy;
}
struct Ball myball = { 500,500,15,5,5 }; // 定义一个自己用按键控制的球,不调用MoveBall,而是自己用按键控制小球移动
void KeyDown() // 控制自己用按键控制的球的函数,通过接收按下的按键,来对小球的参数进行修改
{
	int userKey = _getch();
	switch (userKey)
	{
	case 'w':
	case 'W':
	case '72': // 键盘上的方向键
		myball.y -= 5;
		break;
	case 's':
	case 'S':
	case '80': // 键盘上的方向键
		myball.y += 5;
		break;
	case 'a':
	case 'A':
	case '75': // 键盘上的方向键
		myball.x -= 5;
		break;
	case 'd':
	case 'D':
	case '77': // 键盘上的方向键
		myball.x += 5;
		break;
	}
}
void test_myball()
{
	initgraph(800, 800);
	while (1)
	{
		cleardevice(); // 刷新
		DrawBall(ball); // 首先画出一个球
		DrawBall(myball);
		MoveBall(); // 移动完成后更新了小球的状态,等待下一次进入循环后打印出新状态的小球
		KeyDown();
		Sleep(20); // 延时20ms
	}
    closegraph();
}
int main()
{
	test_myball();
	return 0;
}

此时会发现,自己移动的小球也不动了,只有当我们按了按键让按键控制的球动了之后,自己移动的小球也会移动,一旦我们没按按键,自己移动的小球又停了

这是因为KeyDown函数中的_getch函数是阻塞型函数,当我们没有按按键时,程序会停止在这一步,循环无法继续,所以自己动的小球也不会继续走,这时候我们可以使用_kbhit()函数来判断我们是否按了按键,当我们按了按键,才会去走KeyDown

void test_myball()
{
	initgraph(800, 800);
	while (1)
	{
		cleardevice(); // 刷新
		DrawBall(ball); // 首先画出一个球
		DrawBall(myball);
		MoveBall(); // 移动完成后更新了小球的状态,等待下一次进入循环后打印出新状态的小球
		if(_kbhit())
			KeyDown();
		Sleep(20); // 延时20ms
	}
    closegraph();
}

此时就正常了

玩过之后,会发现自己移动的小球移动起来不是很流畅,这是因为Sleep也是阻塞的,通常会使用定时器去控制自由移动的东西,而不使用Sleep

#include<iostream>
#include<graphics.h>
#include<conio.h>
#include<time.h>
using namespace std;
struct Ball // 球的结构体
{
	int x;   // 球的横坐标
	int y;   // 球的纵坐标
	int r;   // 球的半径
	int dx;  // 球一次向x轴正方向移动的距离
	int dy;  // 球一次向y轴正方向移动的距离
};
struct Ball ball = { 400,400,15,5,-4 }; // 定义一个球
void DrawBall(struct Ball ball) // 画出球的函数
{
	setfillcolor(RED);
	solidcircle(ball.x, ball.y, ball.r);
}
void MoveBall() // 控制球移动的函数
{
	if (ball.x - ball.r <= 0 || ball.x + ball.r >= 800) { ball.dx = -ball.dx; }
	if (ball.y - ball.r <= 0 || ball.y + ball.r >= 800) { ball.dy = -ball.dy; }
	ball.x += ball.dx;
	ball.y += ball.dy;
}
struct Ball myball = { 500,500,15,5,5 }; // 定义一个自己用按键控制的球,不调用MoveBall,而是自己用按键控制小球移动
void KeyDown() // 控制自己用按键控制的球的函数,通过接收按下的按键,来对小球的参数进行修改
{
	int userKey = _getch();
	switch (userKey)
	{
	case 'w':
	case 'W':
	case '72': // 键盘上的方向键
		myball.y -= 5;
		break;
	case 's':
	case 'S':
	case '80': // 键盘上的方向键
		myball.y += 5;
		break;
	case 'a':
	case 'A':
	case '75': // 键盘上的方向键
		myball.x -= 5;
		break;
	case 'd':
	case 'D':
	case '77': // 键盘上的方向键
		myball.x += 5;
		break;
	}
}
int Timer(int duration, int id)
{
	static int startTime[10]; // 创建10个定时器
	int endTime = clock();
	if (endTime - startTime[id] > duration)
	{
		startTime[id] = endTime;
		return 1;
	}
	return 0;
}
void test_myball()
{
	initgraph(800, 800);
	while (1)
	{
		cleardevice(); // 刷新
		DrawBall(ball); // 首先画出一个球
		DrawBall(myball);
		if(Timer(20,0))
			MoveBall(); // 移动完成后更新了小球的状态,等待下一次进入循环后打印出新状态的小球
		if(_kbhit())
			KeyDown();
		// Sleep(20); // 延时20ms
	}
	closegraph();
}
int main()
{
	test_myball();
	return 0;
}

定时器的原理就是使用静态数组,因为静态变量只会初始化一次,所以每当函数调用时,数组中储存的还是上一次的值,并且静态数组不需要初始化,因为静态变量会被自动初始化为0。定时器的第一个参数是多少毫秒就需要让自己移动的小球移动一次,第二个参数是使用下标为id的定时器。clock()函数是基类程序运行到这里用了多长的时间,头文件是time.h。当程序运行到这里的时间减去选中的那个定时器的值大于给定的时间duration时,就表示自己移动的小球需要移动了,将下标为id的那个定时器的值改成clock()的结果,并返回1,表示需要移动。

为了不让用按键控制的小球一闪一闪的,还可以使用双缓冲绘图

开始双缓冲:BeginBatchDraw()

结束双缓冲:EndBatchDraw()

显示一帧:FlushBatchDraw()

void test_myball()
{
	initgraph(800, 800);
	BeginBatchDraw();
	while (1)
	{
		cleardevice(); // 刷新
		DrawBall(ball); // 首先画出一个球
		DrawBall(myball);
		if(Timer(20,0))
			MoveBall(); // 移动完成后更新了小球的状态,等待下一次进入循环后打印出新状态的小球
		if(_kbhit())
			KeyDown();
		// Sleep(20); // 延时20ms
		FlushBatchDraw();
	}
	EndBatchDraw();
	closegraph();
}

因为_getch()是阻塞型的函数,所以在移动自己控制的小球时,会一卡一卡的,为了让小球更流畅,可以使用非阻塞型函数来接收按键的值
 

struct Ball // 球的结构体
{
	int x;   // 球的横坐标
	int y;   // 球的纵坐标
	int r;   // 球的半径
	int dx;  // 球一次向x轴正方向移动的距离
	int dy;  // 球一次向y轴正方向移动的距离
};
struct Ball ball = { 400,400,15,5,-4 }; // 定义一个球
void DrawBall(struct Ball ball) // 画出球的函数
{
	setfillcolor(RED);
	solidcircle(ball.x, ball.y, ball.r);
}
void MoveBall() // 控制球移动的函数
{
	if (ball.x - ball.r <= 0 || ball.x + ball.r >= 800) { ball.dx = -ball.dx; }
	if (ball.y - ball.r <= 0 || ball.y + ball.r >= 800) { ball.dy = -ball.dy; }
	ball.x += ball.dx;
	ball.y += ball.dy;
}
struct Ball myball = { 500,500,15,5,5 }; // 定义一个自己用按键控制的球,不调用MoveBall,而是自己用按键控制小球移动
void KeyDown() // 控制自己用按键控制的球的函数,通过接收按下的按键,来对小球的参数进行修改
{
	int userKey = _getch();
	switch (userKey)
	{
	case 'w':
	case 'W':
	case '72': // 键盘上的方向键
		myball.y -= 5;
		break;
	case 's':
	case 'S':
	case '80': // 键盘上的方向键
		myball.y += 5;
		break;
	case 'a':
	case 'A':
	case '75': // 键盘上的方向键
		myball.x -= 5;
		break;
	case 'd':
	case 'D':
	case '77': // 键盘上的方向键
		myball.x += 5;
		break;
	}
}
void KeyDown2()
{
	if (GetAsyncKeyState(VK_UP))    { myball.y -= 5; }
	if (GetAsyncKeyState(VK_DOWN))  { myball.y += 5; }
	if (GetAsyncKeyState(VK_LEFT))  { myball.x -= 5; }
	if (GetAsyncKeyState(VK_RIGHT)) { myball.x += 5; }
}
int Timer(int duration, int id)
{
	static int startTime[10]; // 创建10个定时器
	int endTime = clock();
	if (endTime - startTime[id] > duration)
	{
		startTime[id] = endTime;
		return 1;
	}
	return 0;
}
void test_myball()
{
	initgraph(800, 800);
	BeginBatchDraw();
	while (1)
	{
		cleardevice(); // 刷新
		DrawBall(ball); // 首先画出一个球
		DrawBall(myball);
		if(Timer(20,0))
			MoveBall(); // 移动完成后更新了小球的状态,等待下一次进入循环后打印出新状态的小球
		/*if(_kbhit())
			KeyDown();*/
		KeyDown2();
		// Sleep(20); // 延时20ms
		FlushBatchDraw();
	}
	EndBatchDraw();
	closegraph();
}
int main()
{
	test_myball();
	return 0;
}

用KeyDown2来替代KeyDown,并且因为是非阻塞型的,所以可以不需要if(_kbhit())来判断了

但此时自己控制的小球会移动的非常快,为了让小球速度正常,可以使用定时器

void test_myball()
{
	initgraph(800, 800);
	BeginBatchDraw();
	while (1)
	{
		cleardevice(); // 刷新
		DrawBall(ball); // 首先画出一个球
		DrawBall(myball);
		if(Timer(20,0))
			MoveBall(); // 移动完成后更新了小球的状态,等待下一次进入循环后打印出新状态的小球
		/*if(_kbhit())
			KeyDown();*/
		if(Timer(20,1))
			KeyDown2();
		// Sleep(20); // 延时20ms
		FlushBatchDraw();
	}
	EndBatchDraw();
	closegraph();
}

此时使用的是下标为1的定时器

6、鼠标交互

当用户点击鼠标的按键时,就会产生一个值,类型是ExMessage,称为鼠标消息,鼠标消息实际上是一个结构体

获取鼠标消息peekmessage(&变量)

讨论鼠标消息 

msg.message:区分鼠标消息的类型,用户按下的值存储在结构体变量msg的message中

msg.x   msg.y: 鼠标当前的坐标

 

void test_mouse()
{
	initgraph(800, 800);
	ExMessage msg;
	// 按左键画圆,按右键画矩形
	while (1)
	{
		while (peekmessage(&msg))
		{
			switch(msg.message) // 根据用户按下的按键,判断在当前鼠标位置画哪一个
			{
			case WM_LBUTTONDOWN:
				circle(msg.x, msg.y, 30);
				break;
			case WM_RBUTTONDOWN:
				rectangle(msg.x - 10, msg.y - 10, msg.x + 10, msg.y + 10);
				break;
			}
		}
	}
	closegraph();
}
int main()
{
	test_mouse();
	return 0;
}

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

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

相关文章

辣子简报芬芳喜事特辑

【辣子简报芬芳喜事特辑】&#x1f389;在这个季节的尾声&#xff0c;当一缕阳光温柔地洒在打包好的行囊上&#xff0c;我们不约而同地停下了忙碌的脚步&#xff0c;回望那段共同编织的璀璨时光——79天的并肩作战&#xff0c;如同一段精彩绝伦的旅程&#xff0c;如今已缓缓驶向…

taoCMS v3.0.2 任意文件读取漏洞(CVE-2022-23316)

前言 CVE-2022-23316 是一个影响 taoCMS v3.0.2 的漏洞。这个漏洞允许攻击者通过 admin.php?actionfile&ctrldownload&path../../1.txt 的路径读取任意文件。攻击者可以利用该漏洞读取服务器上的任何文件&#xff0c;只要他们知道文件的路径​ (OpenCVE)​​ (Tenabl…

Java多语言跨境电商外贸商城源码 tiktok商城系统源码 跨境电商源码

Java多语言跨境电商外贸商城源码 tiktok商城系统源码 跨境电商源码 技术栈 PC端使用&#xff1a;vueelementui 用户端使用&#xff1a;uniapp 管理端使用&#xff1a;vueelementui 后台服务使用&#xff1a;springbootmybatisplusmysql 功能描述&#xff1a; 对接PayPal…

IDEA配Git

目录 前言 1.创建Git仓库&#xff0c;获得可提交渠道 2.选择本地提交的项目名 3.配置远程仓库的地址 4.新增远程仓库地址 5.开始进行commit操作 6.push由于邮箱问题被拒绝的解决方法&#xff1a; 后记 前言 以下操作都是基于你已经下载了Git的前提下进行的&#xff0c…

源码航行阅读目录

&#x1f3c0; 前言 在准备面试和学习的过程中&#xff0c;我阅读了比较多的源码&#xff0c;比如 JUC、Spring、MyBatis&#xff0c;收获了很多代码的设计思想&#xff0c;也对平时调用的 API 有了更深入的理解&#xff1b;但过多散乱的笔记给我的整理复习带来了比较大的麻烦…

前端vue后端java使用easyexcel框架下载表格xls数据工具类

一 使用alibaba开源的 easyexcel框架&#xff0c;后台只需一个工具类即可实现下载 后端下载实现 依赖 pom.xml <dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>4.1.2</version></dependen…

第三方商城对接重构(HF202407)

文章目录 项目背景一、模块范围二、问题方案1. 商品模块整体来说这块对接的不是太顺利,梳理了几条大概的思路:2. 订单模块3. 售后4. 发票5. 结算单经验总结项目背景 作为供应商入围第三方商城成功,然后运营了一段时间,第三方通知要重构, 需要重新对接打通接口完成系统对接…

树型结构数据存储实践

很多业务场景会遇到树形结构的数据&#xff0c;如公司的人员职级树、行政区划树等。 使用类似MySQL的数据库进行存储&#xff0c;需要将树形结构&#xff08;二维&#xff09;存储到行格式&#xff08;一维&#xff09;的db中。 本文介绍了树型结构数据存储的三种方式&#xf…

ShardingSphere实战

ShardingSphere实战 文章目录 ShardingSphere实战分库分表实战建表建表sql利用存储过程建表Sharding-jdbc分库分表配置 基于业务的Sharding-key考虑订单id用户id分片策略订单id的设计与实现**设计思想**&#xff1a;设计思路&#xff1a; 具体分片策略实现测试数据插入商户商品…

计算机提示由于找不到concrt140.dll怎么办,7种解决方法可以对比

在电脑中打开游戏或许软件出现找不到concrt140.dll无法继续执行代码怎么办&#xff1f;concrt140.dll是什么&#xff1f;丢失要怎么解决&#xff1f;下面给大家分析一下concrt140.dll文件是什么与concrt140.dll丢失的多种解决方法&#xff01;相信对你有帮助&#xff01; 一、c…

SpringSecurity6.x使用教程

SpringSecurity6.x使用 SpringSecurity版本 SpringSecurity目前支持的版本如下图所示&#xff0c;可以看到5.x的版本过几年就不会再维护了&#xff0c;6.x将成为主流。 入门 引入依赖 <dependency><groupId>org.springframework.boot</groupId><arti…

法国工程师IMT联盟 密码学及其应用 2022年期末考试

1 密码学 1.1 问题1 对称加密&#xff08;密钥加密) 1.1.1 问题 对称密钥la cryptographie symtrique和公开密钥有哪些优缺点&#xff1f; 1.1.1.1 对称加密&#xff08;密钥加密)的优缺点 1.1.1.1.1 优点 加解密速度快encrypt and decrypt&#xff1a;对称加密算法通常基于…

智能家居安防系统教学解决方案

前言 随着科技的不断进步和智能家居概念的深入人心&#xff0c;智能家居安防系统作为智能家居领域的重要组成部分&#xff0c;其重要性日益凸显。智能家居安防系统不仅能够提供环境和人员的监测功能&#xff0c;还能够采取措施降低或避免人员伤亡及财产损失。因此&#xff0c;…

XXL-JOB中断信号感知

目录 背景 思路 实现逻辑 总结 背景 在使用xxl-job框架时&#xff0c;由于系统是由线程池去做异步逻辑&#xff0c;然后主线程等待&#xff0c;在控制台手动停止时&#xff0c;会出现异步线程不感知信号中断的场景&#xff0c;如下场景 而此时如果人工在控制台停止xxl-job执…

超越YOLO! RT-DETR 实时目标检测技术介绍

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推…

Thisjavabean对象数组

This 1.概念 this是一个对象this是一个构造函数 2.介绍 解决局部变量和成员变量命名冲突 this在面向对象-封装那一篇里&#xff0c;有被两个地方提及。 但我们先简单给一个例子&#xff1a; public Person(String name, String phone, String qqPassword, String bankCar…

【踩坑】修复报错Cannot find DGL libdgl_sparse_pytorch_2.2.0.so

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;欢迎[点赞、收藏、关注]哦~ 目录 错误复现 原因分析 解决方法 错误复现 import dgldataset dgl.data.CoraGraphDataset() graph dataset[0] graph.adjacency_matrix() 原因分…

Python 学习中什么是元组,如何使用元组?

什么是元组 元组&#xff08;Tuple&#xff09;是Python内置的一种数据结构&#xff0c;用于存储多个数据项。与列表类似&#xff0c;元组也可以存储不同类型的数据&#xff0c;但它们之间存在一个重要区别&#xff1a;元组是不可变的&#xff0c;也就是说&#xff0c;一旦创建…

笔记13:switch多分支选择语句

引例&#xff1a; 输入1-5中的任意一共数字&#xff0c;对应的打印字符A,B,C,D,E int num 0; printf("Input a number[1,5]:"); scanf("%d"&#xff0c;&num); if( num 1)printf("A\n"); else if(num2)printf("B\n"); else i…

文化财经macd顶底背离幅图指标公式源码

DIFF:EMA(CLOSE,12) - EMA(CLOSE,26); DEA:EMA(DIFF,9); MACD:2*(DIFF-DEA),COLORSTICK; JC:CROSS(DIFF,DEA); SC:CROSSDOWN(DIFF,DEA); N1:BARSLAST(JC)1; N2:BARSLAST(SC)1; HH:VALUEWHEN(CROSSDOWN(DIFF,DEA),HHV(H,N1));//上次MACD红柱期间合约最大值 HH2:VALUEWHE…