文章目录
- 前言
 - 一、业务系统分析
 - 二、处理配置文件
 - 三、生成界面
 - 四、根据输入事件找到按钮
 - 五、业务系统总流程测试
 - 测试效果:
 - 总结
 
前言
最近看了 电子量产工具 这个项目,本专栏是对该项目的一个总结。
一、业务系统分析

 前面实现了各个子系统:显示、输入、文字、UI、页面,它们只是提供基础能力,跟业务逻辑没有关系。这样的架构很容易扩展,可以在这上面实现各种业务,对于不同的产品,我们只需要写出自己页面函数。
那么对于我们的带电子量产工具,是怎么设置 的主页面呢?
首先 观赏 最终实验效果:
 可以看到在这个主界面上有很多按键,按键上有各自的名字。初始时,按键是红色,当点击按键时,变成绿色。只有一些按键可以通过点击改变颜色,有的可以通过网络输入改变颜色,甚至可以显示进度蓝色。

 可以创建一个配置文件 来描述各个按键的特点:
 
 我们编写 主页面代码时,需要先从配置文件中解析 出数据,再根据数据生成按钮。
 当有事件发生时,就可以找到对应按钮,再 根据配置文案金的数据 执行相应的 点击函数。
 
二、处理配置文件
- 对于配置文件的每一行,都创建一个ItemCfg 结构体。(每一个按钮)
 
typedef struct ItemCfg {
	int index;
	char name[100];
	int bCanBeTouched;				// 是否可以被点击
	char command[100];				// 状态发生变化时 调用的命令
}ItemCfg, *PItemCfg;
 
- 解析配置文件。
 
int ParseConfigFile(void)
{
	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[i];
	}
	return NULL;
}
 
三、生成界面
- 之前 在 页面系统 中写的是 默认的按下函数。
对于按钮,我们要提供自己的OnPressed函数,不适用UI系统默认的函数,这里编写 main_page 按下函数。 
static int MainPageOnPressed(struct Button *ptButton, PDispBuff ptDispBuff, PInputEvent ptInputEvent)
{
	unsigned int dwColor = BUTTON_DEFAULT_COLOR;
	char name[100];
	char status[100];
	char *strButton;
	strButton = ptButton->name;
	
	/* 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;
	}
	else if (ptInputEvent->iType == INPUT_TYPE_NET)
	{
		/* 2. 对于网络事件 */
		
		/* 根据传入的字符串修改颜色 : 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;
	}
	else
	{
		return -1;
	}
	/* 绘制底色 */
	DrawRegion(&ptButton->tRegion, dwColor);
	/* 居中写文字 */
	DrawTextInRegionCentral(strButton, &ptButton->tRegion, BUTTON_TEXT_COLOR);
	/* 刷新界面到 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);
}
 
四、根据输入事件找到按钮
对于触摸屏事件,根据iX、iY找到按钮.
 对于网络数据,我们限定为这样的格式:“name ok”、“name err”、“name 70%”根据name找到按钮.
/*  判断点击的位置  */
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 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;
	}
	return NULL;
}
 
- 重写 main_page.c 的 Run 函数。
 
static void MainPageRun(void *pParams)
{
	int error;
	InputEvent tInputEvent;
	PButton ptButton;
	PDispBuff ptDispBuff = GetDisplayBuffer();
	
	/* 读取配置文件 */
	error = ParseConfigFile();
	if (error)
		return ;
	/* 根据配置文件生成按钮、界面 */
	GenerateButtons();
	while (1)
	{
		/* 读取输入事件 */
		error = GetInputEvent(&tInputEvent);
		if (error)
			continue;
		/* 根据输入事件找到按钮 */
		ptButton = GetButtonByInputEvent(&tInputEvent);
		if (!ptButton)
			continue;
		/* 调用按钮的OnPressed函数 */
		ptButton->OnPressed(ptButton, ptDispBuff, &tInputEvent);
	}
}
 
五、业务系统总流程测试

测试效果:

总结
这个电子量产工具的项目就到这里结束了,后面继续推出 面试笔试的 新专栏,帮助大家避开 笔试的坑。



















