文章目录
- 前言
- 一、流程代码框架
- 1、业务系统框架流程
- 2、主页面流程图
- 3、main.c实现流程
- 二、处理配置文件
- 1、配置文件是啥?
- config.h
- 2、怎么处理配置文件?
- config.c
- 三、生成界面
- 1、计算每个按钮的Region
- 2、逐个生成按钮画面->生成页面
- 四、读取输入事件
- 五、根据输入事件找到按钮
- 六、调用按钮的OnPressed函数
- 完整的main.page.c
- 七、上机测试
前言
我们目前已经搭建了显示系统、输入系统、文字系统、UI系统、页面系统,这就已经完成了这个项目的百分之80了,之间的都是独立的开发基础,我们就是要利用前面的基础来合成再运用产品所需要的逻辑,就可以实现项目功能!
1.前面实现了各个子系统:显示、输入、文字、UI、页面
2.它们只是提供基础能力,跟业务逻辑没有关系
3.这样的架构很容易扩展,可以在这上面实现各种业务
4.比如可以做收银机、自动售卖机、智能称、取号机;
再加上摄像头显示,就可以做出可视对讲、视频监控、人脸红外测温
5.对于不同的产品,我们只需要写出自己页面函数
一、流程代码框架
1、业务系统框架流程
2、主页面流程图
3、main.c实现流程
这里我们在main.c里面做的事情就很明显了,先初始化前面的独立的系统,后面再只要运行业务里面的Run函数即可!
#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>
#include <input_manager.h>
#include <page_manager.h>
int main(int argc, char **argv)
{
int error;
if (argc != 2)
{
printf("Usage: %s <font_file>\n", argv[0]);
return -1;
}
/* 初始化显示系统 */
DisplayInit();
SelectDefaultDisplay("fb");
InitDefaultDisplay();
/* 初始化输入系统 */
InputInit();
IntpuDeviceInit();
/* 初始化文字系统 */
FontsRegister();
/* 选择一个文字系统 */
error = SelectAndInitFont("freetype", argv[1]);
if (error)
{
printf("SelectAndInitFont err\n");
return -1;
}
/* 初始化页面系统 */
PagesRegister();
/* 运行业务系统的主页面 */
Page("main")->Run(NULL);
return 0;
}
那么main函数里面的Run函数里面到底在做啥呢?
要做的东西就只有这些,所以我们后面只需要实现对应作用的函数即可!
二、处理配置文件
1、配置文件是啥?
使用配置文件我们可以使这个代码更有兼容性和更灵活,我们需要修改页面内容时,我们只需要去修改配置文件即可!就不用特地去修改代码,配置文件是怎么样的呢?
按钮的名称都是有配置文件读取得到。
对于每一行,都对应一个按钮图画,但是这些按钮图画有些是可以通过触摸去改变颜色的,但是有一些是不可以的,只能通过网络发送信息来修改颜色,所以我们就用一个结构体来代指一个按钮图画:
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); //通过索引查找config结构体
PItemCfg GetItemCfgByName(char *name); //通过name查找config结构体
#endif
那我们要怎么处理配置文件呢?
2、怎么处理配置文件?
看到了配置文件的格式,我们就知道了,有#号的第一行知识注释,省略这一行其他的就简单了:
#include <config.h>
#include <stdio.h>
#include <string.h>
static ItemCfg g_tItemCfgs[ITEMCFG_MAX_NUM];
static int g_iItemCfgCount = 0;
/* 处理配置文件函数 */
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;
}
config.c
#include <config.h>
#include <stdio.h>
#include <string.h>
static ItemCfg g_tItemCfgs[ITEMCFG_MAX_NUM];
static int g_iItemCfgCount = 0;
//处理配置文件函数
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;
}
//通过索引查找config结构体
PItemCfg GetItemCfgByIndex(int index)
{
if (index < g_iItemCfgCount)
return &g_tItemCfgs[index];
else
return NULL;
}
//通过name查找config结构体
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;
}
三、生成界面
1、计算每个按钮的Region
2、逐个生成按钮画面->生成页面
这个函数是写在main_page.c里面的
static void GenerateButtons(void)
{
int width, height; //宽度高度
int n_per_line; //一行可以画的下多少个按钮
int row, rows; //行(for循环用), 行数
int col; //列数
int n; //按钮个数
PDispBuff pDispBuff;
int xres, yres; //屏幕XY分辨率
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逐个Draw按钮图画 */
for (i = 0; i < n; i++)
g_tButtons[i].OnDraw(&g_tButtons[i], pDispBuff);
}
四、读取输入事件
对于读取输入事件我们之前在输入系统就已经实现了,忘记的可以看看:Linux基础项目开发day02:量产工具——输入系统
五、根据输入事件找到按钮
这些函数都是在main.page.c里面实现的
static int isTouchPointInRegion(int iX, int iY, PRegion ptRegion);
static PButton GetButtonByName(char *name);
static PButton GetButtonByInputEvent(PInputEvent ptInputEvent)
{
int i;
char name[100];
/* 输入事件是触摸屏事件 */
if (ptInputEvent->iType == INPUT_TYPE_TOUCH)
{
for (i = 0; i < g_tButtonCnt; i++)
{
/* 拿触摸点的坐标一个个的去和按钮Region对比,在区域内就返回这个按钮 */
if (isTouchPointInRegion(ptInputEvent->iX, ptInputEvent->iY, &g_tButtons[i].tRegion))
return &g_tButtons[i];//返回这个按钮
}
}
/* 输入事件是网络输入事件 */
else if (ptInputEvent->iType == INPUT_TYPE_NET)
{
//从网络数据中读取数据到name数组中
sscanf(ptInputEvent->str, "%s", name);
//拿网络输入的数据和按钮Region的名称对比,找到一样的返回这个按钮Botton
return GetButtonByName(name);
}
else
{
return NULL;
}
return NULL;
}
/* 拿触摸点的坐标一个个的去和按钮Region对比,在区域内就返回这个按钮 */
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;
}
//拿网络输入的数据和按钮Region的名称对比,找到一样的返回这个按钮Botton
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;
}
六、调用按钮的OnPressed函数
通过第五步我们已经找到了按钮,最后一步就简单了,只需要调用里面的函数即可!
我们在生成按钮界面的时候就已经初始化了Botton结构体里面的函数了
所以我们这里只需要实现函数MainPageOnPressed即可!
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);
/* flush to lcd/web */
FlushDisplayRegion(&ptButton->tRegion, ptDispBuff);
return 0;
}
完整的main.page.c
#include <config.h>
#include <stdio.h>
#include <ui.h>
#include <page_manager.h>
#include <math.h>
#include <string.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;
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);
/* 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;
}
return NULL;
}
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);
}
}
static PageAction g_tMainPage = {
.name = "main",
.Run = MainPageRun,
};
void MainPageRegister(void)
{
PageRegister(&g_tMainPage);
}
七、上机测试
到这里我们就已经完成了基础开发项目了——量产工具
book@100ask:~/28_business_test$ make
book@100ask:~/28_business_test$ arm-buildroot-linux-gnueabihf-gcc -o client unittest/client.c
[root@100ask:~]# mount -t nfs -o nolock,vers=3 192.168.5.11:/home/book/nfs_rootfs /mnt
[root@100ask : /mnt/28_business_test] # mkdir /etc/test_gui
[root@100ask : /mnt/28 business test] # cp etc/test_gui/gui.conf /etc/
[root@100ask : /mnt/28 business test] # ./test ./simsun.ttc &
[root@100ask : /mnt/28 business test] # ./client 192.168.5.9 “net1 ok”
[root@100ask : /mnt/28 business test] # ./client 192.168.5.9 “ALL ok”
[root@100ask : /mnt/28 business test] # ./client 192.168.5.9 “net1 err”
[root@100ask : /mnt/28 business test] # ./client 192.168.5.9 “ALL err”
触摸点击测试:
点击后: