业务总体逻辑
执行main_page的run函数,从配置文件中获取button个数以及是否可被touch以及需要执行的command。根据配置文件生成界面,循环读取输入数据,对不同输入事件进行不同处理,找到有输入的那个button,执行button 的onclicked函数。
static void main_page_run(void *param)
{
int err;
Input_data inputData;
Button *button;
Display_buffer *displayBuffer = get_display_buffer();
/*读取配置文件*/
parse_config_file();
/*根据配置文件生成button和界面*/
generate_buttons();
// printf("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
while (1)
{
/*读取输入事件*/
err = get_input_data(&inputData);
if (err)
continue;
// printf("%s %s %d:get_input_data\n", __FILE__, __FUNCTION__, __LINE__);
/*根据输入事件type找到对应button*/
button = get_button_by_input_data(&inputData);
if (!button)
continue;
// printf("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
/*执行相应button的on_clicked函数,*/
button->on_clicked(button, displayBuffer, &inputData);
}
}
读取配置文件
通过Item_config 表示配置文件的每一行。先解析配置文件,然后提供接口,如根据名字或者index获取一行的信息,获取行数等。
//config文件的每一行为一个Item_config
typedef struct Item_config {
int index;
char name[100];
int can_touch;//button能否点击
char command[100];
} Item_config;
--------------------------------------------------------
# name can_be_touch command
led 1
speaker 1
record 0
key1 0
key2 0
ap3216c 0
icm20608 0
RS485toCAN 0
CANtoRS485 0
4G 0
usb 0
otg_device 0
otg_host 0
serial 0
这里为什么使用fopen而不使用open呢? 使用系统函数open返回的是文件描述符,使用c的标准库函数fopen,返回的是文件指针,我们后面使用fgets需要一个FILE*的参数。很明显,fopen还是调用了open的,增加了io缓冲区。
int parse_config_file(void)
{
FILE *fp;
char buf[100];
char *p = NULL;
/*open config file*/
//fp = fopen(fileName, "r");
fp = fopen(CONFIG_FILE, "r");
// fp = fopen((fileName ? fileName : CONFIG_FILE), "r");
if (!fp) {
printf("can not open config file %s\n", CONFIG_FILE);
return -1;
}
/*读取每一行*/
while (fgets(buf, 100, fp)) {
/*忽略开头的空格或TAB*/
p = buf;
while (*p == ' ' || *p == '\t')
p++;
/*忽略注释 # */
if (*p == '#')
continue;
/*处理每一行*/
itemConfigs[itemConfigCount].command[0] = '\0';//判断是否读到command
itemConfigs[itemConfigCount].index = itemConfigCount;
sscanf(p, "%s %d %s", itemConfigs[itemConfigCount].name, &itemConfigs[itemConfigCount].can_touch,
itemConfigs[itemConfigCount].command);
itemConfigCount++;
}
fclose(fp);
return 0;
}
生成界面
计算每个button的width和height
通过get_itemcfg_count获取按钮数量,通过get_display_buffer获得屏幕xres和yres,高度的话就取宽度的0.618,所以有xresyres=widthwidth0.618n,以此来计算width。为了button与两边保持一定距离更加美观,算出n_per_line 后+1,从而再次计算width会变小一点。
/*算出单个button的width和height*/
n = get_itemcfg_count();
dispBuffer = get_display_buffer();
xres = dispBuffer->xres;
yres = dispBuffer->yres;
width = sqrt(1.0 / 0.618 * xres * yres /
n); //浮点运算 xres*yres=width*width*0.618*n
n_per_line = xres / width + 1; //多显示一个button,再次运算width和height
width = xres / n_per_line;
height = 0.618 * width;
整体居中显示
计算第一个button的起始点坐标,要与屏幕边界有一定裕量,因为经过上面的计算,现在的width和height实际是比xres,yres小的,相减然后居中就除以2得到起始点坐标。
start_x = (xres - width * (n_per_line-1)) / 2; //第一个buttond的左顶点x,y
rows = n / n_per_line;
if (rows * n_per_line < n)
{
/*rows为小数向下取整,rows+1*/
rows++;
}
start_y = (yres - rows * height) / 2;
然后计算每个button的region,对每个button进行init_button。通过两for循环,一个一个button来计算,计算当前btn的region需要知道前一个btn的起始点坐标。pre_start_y = start_y + row * height,pre_start_x =start_x - width;
init_button参数应该传入main_page的on_clicked函数。
/*计算每个button 的region*/
for (row = 0; row < rows; row++)
{
pre_start_y = start_y + row * height;
pre_start_x =
start_x - width; // 第一个button的x= (start_x-width) +width
for (col = 0; (col < n_per_line) && (i < n); col++)
{
button = &buttons[i]; //计算buttons里的每一个button的region
button->btn_region.x = pre_start_x + width; //前面一个button的x+width
button->btn_region.y = pre_start_y;
button->btn_region.width = width - X_GAP;
button->btn_region.height = height - Y_GAP;
pre_start_x = button->btn_region.x;
/*init_button*/
init_button(button, get_itemcfg_by_index(i)->name, NULL, NULL,
main_page_on_clicked);
i++;
}
}
while循环执行业务
重点就是业务on_clicked函数的实现。
while (1)
{
/*读取输入事件*/
err = get_input_data(&inputData);
if (err)
continue;
// printf("%s %s %d:get_input_data\n", __FILE__, __FUNCTION__, __LINE__);
/*根据输入事件type找到对应button*/
button = get_button_by_input_data(&inputData);
if (!button)
continue;
// printf("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
/*执行相应button的on_clicked函数,*/
button->on_clicked(button, displayBuffer, &inputData);
}
main_page的on_clicked函数
通过判断输入数据的TYPE类型分辨出是touch事件还是网络输入,==注意方便touch时要 && inputData->presure 判断有无触摸。==之后还要判断配置文件中该button是否支持touch。
通过system函数将新构建的command执行。
/*init_button时传入此函数*/
static int main_page_on_clicked(struct Button *btn,
Display_buffer *buffer,
Input_data *inputData)
{
unsigned int color = BUTTON_DEFAULT_COLOR;
char name[100];
char netInputStatus[100];
/*最后添加的,支持配置文件的command*/
char *commandStatus[3] = {
"err", "ok",
"percent"}; //可以根据配置文件的command的不同参数需求更改此数组。
int commandStatusIndex = 0; //默认err
char command[1000];
Item_config *itemConfig;
char *buttonName = btn->name; //百分比显示的text需要设置
/*对于touch事件,先判断button是否支持touch事件*/
if (inputData->type == INPUT_TYPE_TOUCH && inputData->presure)
{
//不支持touch
if (get_itemcfg_by_name(btn->name)->can_touch == 0)
return -1;
/*如果支持touch,变换颜色*/
btn->status = !btn->status; //每次点击,状态翻转,每次点击都会换颜色。
if (btn->status)
{
color = BUTTON_CLICKED_COLOR;
commandStatusIndex = 1; // ok
}
}
/*对于net事件,根据传进来的字符串修改相应颜色*/
else if (inputData->type == INPUT_TYPE_NET)
{
printf("%s %s %d : a net_input\n", __FILE__, __FUNCTION__, __LINE__);
sscanf(inputData->str, "%s %s", name, netInputStatus);
if (strcmp(netInputStatus, "ok") == 0)
{
color = BUTTON_CLICKED_COLOR;
commandStatusIndex = 1; // ok
}
else if (strcmp(netInputStatus, "err") == 0)
{
color = BUTTON_DEFAULT_COLOR;
commandStatusIndex = 0; // err
}
else if (netInputStatus[0] >= '0' && netInputStatus[0] <= '9')
{
color = BUTTON_PERCENT_COLOR;
buttonName = netInputStatus; // button text设置为百分比
commandStatusIndex = 2;
}
else
return -1;
}
else
return -1;
draw_region(&btn->btn_region, color);
draw_text_central(buttonName, &btn->btn_region, BUTTON_TEXT_COLOR);
flush_display_region(&btn->btn_region, buffer);
/*执行command*/
itemConfig = get_itemcfg_by_name(btn->name);
if (itemConfig->command[0] != '\0')
{
//构造出新的command 加上ok err
// percent给sh脚本提供参数,脚本里面使用$1即可得到该参数!
sprintf(command, "%s %s", itemConfig->command,
commandStatus[commandStatusIndex]);
system(command); //相当于在shell上输入 command,不仅仅只能是.sh
}
return 0;
}
改善文字显示效果
之前的文字大小计算方法如果名字较长的话字会非常小,这里我们取名字最长的那个button的名字,来适配字体大小,具体见代码 的注释。
//后续添加以改善文字的显示效果
static int get_font_size_for_all_button(void)
{
int maxLen = -1;
int maxIndex;
int len;
Cartesian_region cartesianRegion;
float k, kx, ky;
/*找出文字最长的button*/
for (int i = 0; i < n; i++)
{
len = strlen(buttons[i].name);
if (len > maxLen)
{
maxLen = len;
maxIndex = i;
}
}
/*以font_size = 100,算出它的bbox框*/
font_set_size(100);
font_get_text_bbox(buttons[maxIndex].name, &cartesianRegion);
/*把文字的bbox缩放为button的bbox(region)*/
kx = (float)buttons[maxIndex].btn_region.width / cartesianRegion.width;
ky = (float)buttons[maxIndex].btn_region.height / cartesianRegion.height;
if (kx < ky)
k = kx;
else
k = ky;
/*反算出合适的font_size,为保证足够边界,取0.8吧*/
return k * 100 * 0.8;
}