inux基础项目开发1:量产工具——业务系统(七)

news2025/3/10 18:42:23

前言:

前面我们已经构造出来显示系统、输入系统、文字系统、UI系统、页面系统,这个项目百分之八十需要实现的都已经构建出来了,最后让我们对这个项目进行最后一项系统的搭建,也就是业务系统,说到业务大家应该就知道我们要干什么了,那就是把所有的代码整合到一起实现一些我们想要实现的功能。

目录

一、流程代码框架

1.业务系统流程图

 2.主页面流程图:

3.main.c

4.main_page.c 

二、处理配置文件

1.配置文件的样子 

2. 怎么处理配置文件

3.config.h

4.config.c 

三、生成界面 

1.计算每个按钮的Region

 2.main_page.c

 四、处理输入事件

 man_page.c


一、流程代码框架

1.业务系统流程图

 2.主页面流程图:

3.main.c


#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/fb.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <stdlib.h>

#include <disp_manager.h>
#include <font_manager.h>


int main(int argc, char **argv)
{
	PDispBuff ptBuffer;
	int error;

	if (argc != 2)
	{
		printf("Usage: %s <font_file>\n", argv[0]);
		return -1;
	}
	
	/* 初始化显示系统 */		
	DisplayInit();

	SelectDefaultDisplay("fb");

	InitDefaultDisplay();

	ptBuffer = GetDisplayBuffer();

	/* 初始化输入系统 */		
	InputInit();
	IntpuDeviceInit();


	/* 初始化文字系统 */		
	FontsRegister();
	
	error = SelectAndInitFont("freetype", argv[1]);
	if (error)
	{
		printf("SelectAndInitFont err\n");
		return -1;
	}

	/* 初始化页面系统 */		
	PagesRegister();

	/* 运行业务系统的主页面 */
	Page("main")->Run(NULL);
	
	return 0;	
}

第22~26行:打印用法 

第29~35行:初始化显示系统

        第29行:初始化显示设备

        第30行:选择默认的显示设备

        第31行:初始化这个默认的设备

        第35行:获得显示区域

第38、39行:初始化输入系统

        第38行:初始化输入设备

        第39行:选择默认输入设备

第43~50行:初始化文字系统

        第45行:选择字库

第53行:初始化页面系统

第55行:运行业务系统的主页面

4.main_page.c 


#include <page_manager.h>
#include <stdio.h>

static void MainPageRun(void *pParams)
{
	/* 读取配置文件 */

	/* 根据配置文件生成按钮、界面 */

	while (1)
	{
		/* 读取输入事件 */

		/* 根据输入事件找到按钮 */

		/* 调用按钮的OnPressed函数 */
	}
}

static PageAction g_tMainPage = {
	.name = "main",
	.Run  = MainPageRun,
};

void MainPageRegister(void)
{
	PageRegister(&g_tMainPage);
}


二、处理配置文件

        对于我们要制作的页面,里面有很多按钮,那么我们怎么知道每个按钮里面都存放着什么呢?这时我们就需要配置文件,以后我们想要打开哪个按钮,调整测试模块时,可以直接修改配置文件即可,就不需要重新修改,编译程序就可以带来非常大的灵活性。

1.配置文件的样子 

2. 怎么处理配置文件

对于配置文件的每一行,都创建一个ItemCfg结构体

 并对外提供函数,比如获得某个配置项

3.config.h


#ifndef _CONFIG_H
#define _CONFIG_H

#include <common.h>

#define ITEMCFG_MAX_NUM 30
#define CFG_FILE  "/etc/test_gui/gui.conf"

typedef struct ItemCfg {
	int index;
	char name[100];
	int bCanBeTouched;
	char command[100];
}ItemCfg, *PItemCfg;

int ParseConfigFile(void);
int GetItemCfgCount(void);
PItemCfg GetItemCfgByIndex(int index);
PItemCfg GetItemCfgByName(char *name);


#endif

第7行:解析配置文件,得到的结构体保存到数组里,数组的内存大小为30

第8行:提供默认的配置文件

第11行:描述配置文件的哪一项

第12行:描述配置文件的名字

第13行:描述能否被点击

第14行:状态发生变化时调用某个命令

第17行:传入配置文件的名字解析配置文件,得到ItemCfg结构体

第18行:确定有多少行,也就是确定出有多少个按钮

第19行:传入某个index,返回某个配置项

第20行:传入名字,返回某个配置项

4.config.c 



#include <config.h>
#include <stdio.h>

static ItemCfg g_tItemCfgs[ITEMCFG_MAX_NUM];
static int g_iItemCfgCount = 0;

int ParseConfigFile(char *strFileName)
{
	FILE *fp;
	char buf[100];
	char *p = buf;
	
	/* 1. open config file */
	fp = fopen(CFG_FILE, "r");
	if (!fp)
	{
		printf("can not open cfg file %s\n", CFG_FILE);
		return -1;
	}

	while (fgets(buf, 100, fp))
	{
		/* 2.1 read each line */
		buf[99] = '\0';		

		/* 2.2 吃掉开头的空格或TAB */
		p = buf;
		while (*p == ' ' || *p =='\t')
			p++;

		/* 2.3 忽略注释 */
		if (*p == '#')
			continue;

		/* 2.4 处理 */
		g_tItemCfgs[g_iItemCfgCount].command[0] = '\0';
		g_tItemCfgs[g_iItemCfgCount].index = g_iItemCfgCount;
		sscanf(p, "%s %d %s", g_tItemCfgs[g_iItemCfgCount].name, &g_tItemCfgs[g_iItemCfgCount].bCanBeTouched, \
		                      g_tItemCfgs[g_iItemCfgCount].command);
		g_iItemCfgCount++;		
	}
	return 0;
}

int GetItemCfgCount(void)
{
	return g_iItemCfgCount;
}

PItemCfg GetItemCfgByIndex(int index)
{
	if (index < g_iItemCfgCount)
		return &g_tItemCfgs[index];
	else
		return NULL;
}

PItemCfg GetItemCfgByName(char *name)
{
	int i;
	for (i = 0; i < g_iItemCfgCount; i++)
	{
		if (strcmp(name, g_tItemCfgs[i].name) == 0)
			return &g_tItemCfgs[index];
	}
	return NULL;
}

第6行:解析配置文件,得到ItemCfg结构体保存到g_tItemCfgs[ITEMCFG_MAX_NUM]这个数组里

第7行:解析配置文件以后,记录配置文件有多少项

int ParseConfigFile(char *strFileName)

第9~45行: 进行解析配置文件

        第13行:读进一行放到buf数组里

        第16行:打开文件 ,只需要读(r)即

        第19行:如果没有打开的话,打印不能打开这个文件,表示配置失败

        第23行:fgets(buf, 100, fp)读入一行

        第26行:读出每一行,最大数量是100

        第29~31行:吃掉开头的空格TAB

        第34~35行:忽略注释

        第38~42行:进行处理

                第38行:根据command[0] 是否为 '\0' 来判断g_tItemCfgs里是否有命令,如果第0项没有command就表明command从配置文件里面得到了他的值

        第44行:返回0表示成功

int GetItemCfgCount(void)

第47~50行:确定有多少行,也就是确定出有多少个按钮,直接获得g_iItemCfgCount即可

PItemCfg GetItemCfgByIndex(int index)

第52~58行:通过index获得某一项

PItemCfg GetItemCfgByName(char *name)

第60~69行:通过名字获得某一项

三、生成界面 

想显示这样的界面

通过配置文件可以知道有哪些按钮,知道每个按钮的名字

只剩最后一项了:怎么确定每个按钮的位置、大小?需要计算!

1.计算每个按钮的Region

 2.main_page.c


#include <config.h>
#include <page_manager.h>
#include <stdio.h>
#include <math.h>

#define X_GAP 5
#define Y_GAP 5

static Button g_tButtons[ITEMCFG_MAX_NUM];


static void GenerateButtons(void)
{
	int width, height;
	int n_per_line;
	int row, rows;
	int col;
	int n;
	PDispBuff pDispBuff;
	int xres, yres;
	int start_x, start_y;
	int pre_start_x, pre_start_y;
	PButton pButton;
	int i = 0;
	
	/* 算出单个按钮的width/height */
	n = GetItemCfgCount();
	
	pDispBuff = GetDisplayBuffer();
	xres = pDispBuff->iXres;
	yres = pDispBuff->iYres;
	width = sqrt(1.0/0.618 *xres * yres / n);
	n_per_line = xres / width + 1;
	width  = xres / n_per_line;
	height = 0.618 * width;	

	/* 居中显示:  计算每个按钮的region  */
	start_x = (xres - width * n_per_line) / 2;
	rows    = n / n_per_line;
	if (rows * n_per_line < n)
		rows++;
	
	start_y = (yres - rows*height)/2;

	/* 计算每个按钮的region */
	for (row = 0; (row < rows) && (i < n); row++)
	{
		pre_start_y = start_y + row * height;
		pre_start_x = start_x - width;
		for (col = 0; (col < n_per_line) && (i < n); col++)
		{
			pButton = g_tButtons[i];
			pButton->tRegion.iLeftUpX = pre_start_x + width;
			pButton->tRegion.iLeftUpY = pre_start_y;
			pButton->tRegion.iWidth   = width - X_GAP;
			pButton->tRegion.iHeigh   = height - Y_GAP;
			pre_start_x = pButton->tRegion.iLeftUpX;

			/* InitButton */
			InitButton(pButton, GetItemCfgByIndex(i)->name, NULL, NULL, NULL);
			i++;
		}
	}

	/* OnDraw */
	for (i = 0; i < n; i++)
		g_tButtons[i].OnDraw(&g_tButtons[i], pDispBuff);
}

static void MainPageRun(void *pParams)
{
	int error;
	/* 读取配置文件 */
	error = ParseConfigFile();
	if (error)
		return error;

	/* 根据配置文件生成按钮、界面 */
	GenerateButtons();

	while (1)
	{
		/* 读取输入事件 */

		/* 根据输入事件找到按钮 */

		/* 调用按钮的OnPressed函数 */
	}
}

static PageAction g_tMainPage = {
	.name = "main",
	.Run  = MainPageRun,
};

void MainPageRegister(void)
{
	PageRegister(&g_tMainPage);
}


第77~75行:读取配置文件

第80~89行:根据配置文件生成按钮、界面

static void GenerateButtons(void)

第13~69行:生成按钮

        第28~36行:算出单个按钮的width/height

                第28行:算出有多少个按钮

                第30~32行:获得屏幕分辨率

                第33行:算出按钮理想宽度

                第34行:算出每一行最多显示出多少个按钮

                第35行:再反算出实际按钮宽度

                第36行:根据宽度算出高度

        第39~44行:居中显示:  计算每个按钮的区域region

                第39~44行:算出左上角的具体位置

                          第40行:算出有多少行

                          第41行:如果rows * n_per_line < n则有小数出现,需要给rows++

        第46~64行:计算每个按钮的区域region

                第49、50行:前一个按钮的位置

                第54、55行:计算下一个按钮的位置

                第56、57行:计算下一个按钮的宽度和高度,并且留5个距离的间隙 

                第58行:更新起始位置

                第61行:初始化按钮

                                GetItemCfgByIndex(i)->name从配置文件中得到

                        

void InitButton(PButton ptButton, char *name, PRegion ptRegion, ONDRAW_FUNC OnDraw, ONPRESSED_FUNC OnPressed)
{
	ptButton->status = 0;
	ptButton->name = name;
	if (ptRegion)
		ptButton->tRegion = *ptRegion;
	ptButton->OnDraw    = OnDraw ? OnDraw : DefaultOnDraw;
	ptButton->OnPressed = OnPressed ? OnPressed : DefaultOnPressed;
}

        第67~68行:显示出按钮

  

 四、处理输入事件

我们得到的输入事件,可能来自触摸屏,也可能是其他APP发来的网络数据

对于触摸屏事件,根据iXiY找到按钮 

对于网络数据,我们限定为这样的格式:

  “name ok”、“name err”、“name 70%

  根据name找到按钮

对于按钮,我们要提供自己的OnPressed函数,不适用UI系统默认的函数

 man_page.c


#include <config.h>
#include <page_manager.h>
#include <stdio.h>
#include <math.h>

#define X_GAP 5
#define Y_GAP 5

static Button g_tButtons[ITEMCFG_MAX_NUM];
static int g_tButtonCnt;

static int MainPageOnPressed(struct Button *ptButton, PDispBuff ptDispBuff, PInputEvent ptInputEvent)
{
	unsigned int dwColor = BUTTON_DEFAULT_COLOR;
	char name[100];
	char status[100];
	char *strButton;
	
	/* 1. 对于触摸屏事件 */
	if (ptInputEvent->iType == INPUT_TYPE_TOUCH)
	{
		/* 1.1 分辨能否被点击 */
		if (GetItemCfgByName(ptButton->name)->bCanBeTouched == 0)
			return -1;

		/* 1.2 修改颜色 */
		ptButton->status = !ptButton->status;
		if (ptButton->status)
			dwColor = BUTTON_PRESSED_COLOR;

		strButton = ptButton->name;
	}
	else if (ptInputEvent->iType == INPUT_TYPE_NET)
	{
		/* 2. 对于网络事件 */
		strButton = ptButton->name;
		
		/* 根据传入的字符串修改颜色 : wifi ok, wifi err, burn 70 */
		sscanf(ptInputEvent->str, "%s %s", name, status);
		if (strcmp(status, "ok") == 0)
			dwColor = BUTTON_PRESSED_COLOR;
		else if (strcmp(status, "err") == 0)
			dwColor = BUTTON_DEFAULT_COLOR;
		else if (status[0] >= '0' && status[0] <= '9')
		{			
			dwColor = BUTTON_PERCENT_COLOR;
			strButton = status;			
		}
		else
			return -1;
		
	}

	/* 绘制底色 */
	DrawRegion(&ptButton->tRegion, dwColor);

	/* 居中写文字 */
	DrawTextInRegionCentral(strButton, &ptButton->tRegion, BUTTON_TEXT_COLOR);

	/* flush to lcd/web */
	FlushDisplayRegion(&ptButton->tRegion, ptDispBuff);
	return 0;
}


static void GenerateButtons(void)
{
	int width, height;
	int n_per_line;
	int row, rows;
	int col;
	int n;
	PDispBuff pDispBuff;
	int xres, yres;
	int start_x, start_y;
	int pre_start_x, pre_start_y;
	PButton pButton;
	int i = 0;
	
	/* 算出单个按钮的width/height */
	g_tButtonCnt = n = GetItemCfgCount();
	
	pDispBuff = GetDisplayBuffer();
	xres = pDispBuff->iXres;
	yres = pDispBuff->iYres;
	width = sqrt(1.0/0.618 *xres * yres / n);
	n_per_line = xres / width + 1;
	width  = xres / n_per_line;
	height = 0.618 * width;	

	/* 居中显示:  计算每个按钮的region  */
	start_x = (xres - width * n_per_line) / 2;
	rows    = n / n_per_line;
	if (rows * n_per_line < n)
		rows++;
	
	start_y = (yres - rows*height)/2;

	/* 计算每个按钮的region */
	for (row = 0; (row < rows) && (i < n); row++)
	{
		pre_start_y = start_y + row * height;
		pre_start_x = start_x - width;
		for (col = 0; (col < n_per_line) && (i < n); col++)
		{
			pButton = g_tButtons[i];
			pButton->tRegion.iLeftUpX = pre_start_x + width;
			pButton->tRegion.iLeftUpY = pre_start_y;
			pButton->tRegion.iWidth   = width - X_GAP;
			pButton->tRegion.iHeigh   = height - Y_GAP;
			pre_start_x = pButton->tRegion.iLeftUpX;

			/* InitButton */
			InitButton(pButton, GetItemCfgByIndex(i)->name, NULL, NULL, MainPageOnPressed);
			i++;
		}
	}

	/* OnDraw */
	for (i = 0; i < n; i++)
		g_tButtons[i].OnDraw(&g_tButtons[i], pDispBuff);
}

static int isTouchPointInRegion(int iX, int iY, PRegion ptRegion)
{
	if (iX < ptRegion->iLeftUpX || iX >= ptRegion->iLeftUpX + ptRegion->iWidth)
		return 0;

	if (iY < ptRegion->iLeftUpY || iY >= ptRegion->iLeftUpY + ptRegion->iHeigh)
		return 0;

	return 1;
}


static PButton GetButtonByName(char *name)
{
	int i;
	
	for (i = 0; i < g_tButtonCnt; i++)
	{
		if (strcmp(name, g_tButtons[i].name) == 0)
			return &g_tButtons[i];
	}

	return NULL;
}


static PButton GetButtonByInputEvent(PInputEvent ptInputEvent)
{
	int i;
	char name[100];
	
	if (ptInputEvent->iType == INPUT_TYPE_TOUCH)
	{
		for (i = 0; i < g_tButtonCnt; i++)
		{
			if (isTouchPointInRegion(ptInputEvent->iX, ptInputEvent->iY, &g_tButtons[i].tRegion))
				return &g_tButtons[i];
		}
	}
	else if (ptInputEvent->iType == INPUT_TYPE_NET)
	{
		sscanf(ptInputEvent->str, "%s", name);
		return GetButtonByName(name);
	}
	else
	{
		return NULL;
	}
}

static void MainPageRun(void *pParams)
{
	int error;
	InputEvent tInputEvent;
	PButton ptButton;
	PDispBuff ptDispBuff = GetDisplayBuffer();
	
	/* 读取配置文件 */
	error = ParseConfigFile();
	if (error)
		return error;

	/* 根据配置文件生成按钮、界面 */
	GenerateButtons();

	while (1)
	{
		/* 读取输入事件 */
		error = GetInputEvent(&tInputEvent);
		if (error)
			continue;

		/* 根据输入事件找到按钮 */
		ptButton = GetButtonByInputEvent(&tInputEvent);
		if (!ptButton)
			continue;

		/* 调用按钮的OnPressed函数 */
		ptButton->OnPressed(ptButton, ptDispBuff, &tInputEvent);
	}
}

static PageAction g_tMainPage = {
	.name = "main",
	.Run  = MainPageRun,
};

void MainPageRegister(void)
{
	PageRegister(&g_tMainPage);
}

第82行:计算按钮的个数 

第115行:初始化按钮时候提供我们自己的处理函数 MainPageOnPressed

第190~204行:

        第193~195行:读取输入事件,得到的数据保存在tInputEvent这个结构体里

        第198~200行:根据输入事件找到按钮

        第203行:调用按钮的OnPressed函数

static PButton GetButtonByInputEvent(PInputEvent ptInputEvent)

第151~173行:根据输入事件找到按钮

        第156~163行:判断事件的类型是否为触摸屏

                第160行:遍历每个按钮并且找寻触点

        第164~168行:判断事件的类型是否为网络数据

                第166行:将网络数据名字提取出来

                第167行:根据名字找到按钮

        

static int isTouchPointInRegion(int iX, int iY, PRegion ptRegion)

第125~134行:寻找触点 

static PButton GetButtonByName(char *name)

 第137~148行:根据网络数据输入的名字找到按钮

static int MainPageOnPressed(struct Button *ptButton, PDispBuff ptDispBuff, PInputEvent ptInputEvent)

 第13~64行:之前是默认的处理函数,现在我们要提供自己自定义的处理函数

         第15行:设置默认按钮颜色为红色

         第21行:判断是否是触摸屏事件

                 第24行:判断是否能被点击

                        第28行:修改颜色

        第34行:判断是否为网络事件  

                第40~51行:根据传入的字符串进行判断是否修改颜色

                在ui.h里面多定义一个颜色”蓝色“

#define BUTTON_PERCENT_COLOR 0x0000ff

 献上韦东山老师对以上代码流程的梳理过程

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

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

相关文章

软件生命周期四个阶段SDLC

软件产品生命周期&#xff1a;指软件产品研发全部过程、活动和任务的结构框架。 产品的生命周期一般包括四个阶段&#xff1a;引入期、成长期、成熟期和衰退期&#xff0c;在不同的阶段中&#xff0c;市场对产品的反应不同&#xff0c;其销售特点不同&#xff0c;因而产品管理的…

Windows驱动中使用数字签名验证控制设备访问权限

1. 背景 在一般的驱动开发时&#xff0c;创建了符号链接后在应用层就可以访问打开我们的设备并进行通讯。 但我们有时候不希望非自己的进程访问我们的设备并进行交互&#xff0c;虽然可以使用 IoCreateDeviceSecure 来创建有安全描述符的设备&#xff0c;但大数的用户账户为了方…

【理解ARM架构】中断处理 | CPU模式

&#x1f431;作者&#xff1a;一只大喵咪1201 &#x1f431;专栏&#xff1a;《理解ARM架构》 &#x1f525;格言&#xff1a;你只管努力&#xff0c;剩下的交给时间&#xff01; 目录 &#x1f35c;中断&#x1f368;GPIO中断代码实现 &#x1f35c;CPU&#x1f368;CONTROL…

设计模式-结构型模式之装饰者设计模式

文章目录 六、装饰者模式 六、装饰者模式 装饰者模式&#xff08;Decorator Pattern&#xff09;允许向一个现有的对象添加新的功能&#xff0c;同时又不改变其结构。它是作为现有的类的一个包装。 装饰类和被装饰类可以独立发展&#xff0c;不会相互耦合&#xff0c;装饰者模…

各大期刊网址

AAAL: http://dblp.uni-trier.de/db/conf/aaai/ CVPR: http://dblp.uni-trier.de/db/conf/cvpr/ NeurlPS:http://dblp.uni-trier.de/db/conf/nips/ ICCV: http://dblp.uni-trier.de/db/conf/iccv/ IJCAL: http://dblp.uni-trier.de/db/conf/ijcal/ 并非原创引…

Linux(12):磁盘配额(Quota)与进阶文件系统管理

磁盘配额&#xff08;Quota&#xff09;的应用与实作 Quota 的一般用途&#xff1a; 针对 www server &#xff0c;例如:每个人的网页空间的容量限制&#xff1b; 针对 mail server&#xff0c;例如:每个人的邮件空间限制。 针对 file server&#xff0c;例如:每个人最大的可用…

【开源】基于Vue+SpringBoot的康复中心管理系统

项目编号&#xff1a; S 056 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S056&#xff0c;文末获取源码。} 项目编号&#xff1a;S056&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 普通用户模块2.2 护工模块2.3 管理员…

详解Spring对Mybatis等持久化框架的整合

&#x1f609;&#x1f609; 学习交流群&#xff1a; ✅✅1&#xff1a;这是孙哥suns给大家的福利&#xff01; ✨✨2&#xff1a;我们免费分享Netty、Dubbo、k8s、Mybatis、Spring...应用和源码级别的视频资料 &#x1f96d;&#x1f96d;3&#xff1a;QQ群&#xff1a;583783…

最大单词数算法分析

题目描述&#xff1a; 算法一&#xff1a; 代码实现&#xff1a; # include<stdio.h> # include<string.h>int main(){//char text[100]"leet code";//char brokenLetters[26]"lt";char text[100]"hello world";char brokenLetters…

28、DS18B20温度传感器

DS18B20介绍 DS18B20是一种常见的数字温度传感器&#xff0c;其控制命令和数据都是以数字信号的方式输入输出&#xff0c;相比较于模拟温度传感器&#xff0c;具有功能强大、硬件简单、易扩展、抗干扰性强等特点 测温范围&#xff1a;-55C 到 125C 通信接口&#xff1a;1-Wire…

常见的线程安全问题及解决

1. 什么是线程安全 线程安全指的是当多个线程同时访问一个共享的资源时&#xff0c;不会出现不确定的结果。这意味着无论并发线程的调度顺序如何&#xff0c;程序都能够按照设计的预期来运行&#xff0c;而不会产生竞态条件&#xff08;race condition&#xff09;或其他并发问…

【数据库原理】函数依赖、三范式、视图、事务、数据库设计(概念、逻辑、物理结构设计,实施)、数据流图、数据字典、存储过程、触发器、备份与还原【我统统拿下!】

函数依赖 函数依赖是关系数据库中的重要概念&#xff0c;用于描述关系中属性之间的依赖关系。 在关系数据库中&#xff0c;如果关系 R 中的某个属性或属性组的值能够唯一确定关系中其他属性的值&#xff0c;那么我们就说这个属性或属性组对其他属性具有函数依赖关系。 举个例…

pytest系列——allure之在测试用例添加标题(@allure.title())

前言 通过使用装饰器allure.title可以为测试用例自定义一个更具有阅读性的易读的标题。 allure.title的三种使用方式&#xff1a; 直接使用allure.title为测试用例自定义标题&#xff1b;allure.title支持通过占位符的方式传递参数&#xff0c;可以实现测试用例标题参数化&a…

SpringBootCache缓存——j2cache

文章目录 缓存供应商变更&#xff1a;j2cache 缓存供应商变更&#xff1a;j2cache <!-- https://mvnrepository.com/artifact/net.oschina.j2cache/j2cache-core --><dependency><groupId>net.oschina.j2cache</groupId><artifactId>j2cache-cor…

【数组和函数实战: 斗地主游戏】

目录 1. 玩法说明 2. 分析和设计 3. 代码实现 4. 游戏演示1. 玩法说明 一副54张牌,3最小,两个王最大,其实是2,和上面一样从大到小排列 2. 分析和设计 2.1 分析和设计 常量和变量设计 一副牌有54张,有牌的数值和花色,可以分别用两个数组来存储,card为卡牌表示的数值,color为…

WebGL笔记:矩阵平移的数学原理和实现

矩阵平移的数学原理 让向量OA位移 x方向&#xff0c;txy方向&#xff0c;tyz方向&#xff0c;tz 最终得到向量OB 矩阵平移的应用 再比如我要让顶点的x移动0.1&#xff0c;y移动0.2&#xff0c;z移动0.3 1 &#xff09;顶点着色器核心代码 <script id"vertexShader&…

echarts实现全国及各省市地图

echarts实现全国及各省市地图&#xff08;内附地图json文件&#xff09; 去阿里云就可以获取&#xff1a;[阿里云地理]&#xff1a;http://datav.aliyun.com/portal/school/atlas/area_selector#&lat31.769817845138945&lng104.29901249999999&zoom4(http://datav…

6.7 Windows驱动开发:内核枚举LoadImage映像回调

在笔者之前的文章《内核特征码搜索函数封装》中我们封装实现了特征码定位功能&#xff0c;本章将继续使用该功能&#xff0c;本次我们需要枚举内核LoadImage映像回调&#xff0c;在Win64环境下我们可以设置一个LoadImage映像加载通告回调&#xff0c;当有新驱动或者DLL被加载时…

HTML块元素和行内元素

HTML块元素和行内元素 1.分类2.块元素3.行内元素 1.分类 在HTML中&#xff0c;根据元素的表现形式&#xff0c;一般可以分为两类&#xff1a; 块元素&#xff08;block&#xff09;行内元素&#xff08;inline&#xff09; 2.块元素 在HTML中&#xff0c;块元素在浏览器显示…

制作一个RISC-V的操作系统二-RISC-V ISA介绍

文章目录 ISA的基本介绍啥是ISA为什么要设计ISACISCvsRISCISA的宽度知名ISA介绍 RISC-V历史和特点RISC-V发展RISC-V ISA 命名规范模块化的ISA通用寄存器Hart特权级别Control and Status Register&#xff08;CSR&#xff09;内存管理与保护异常和中断 ISA的基本介绍 啥是ISA …