Linux应用项目之量产工具(四)——UI系统

embedded/2024/11/22 4:36:45/

前言

        前面我们完成了量产工具的显示、输入和文字系统,如下:

量产工具(一)——显示系统

量产工具(二)——输入系统

量产工具(三)——文字系统

项目框架

        本节我们来实现量产工具的第四个子系统——UI系统。


数据结构抽象

        所谓 UI ,就是 User Interface(用户界面),有图像界面(GUI)等;我们的UI系统,就是构造各类GUI元素,比如按钮(目前只实现按钮)

        怎么描述一个按钮呢?它的位置、大小怎么表示?怎么绘制它?用户点击它之后,如何处理?

        根据上面这些参数需求,我们可以构造如下结构体:

ui.h

#ifndef _UI_H
#define _UI_H#include <common.h>//通用头文件,定义了Region结构体
#include <disp_manager.h>
#include <input_manager.h>#define BUTTON_DEFAULT_COLOR 0xff0000 //默认的按键颜色为红色
#define BUTTON_PRESSED_COLOR 0x00ff00 //按键按下后的颜色为绿色
#define BUTTON_TEXT_COLOR    0x000000 //文本的颜色为黑色typedef struct Button Button;
typedef struct Button * PButton;/*用typedef将函数指针更名为第一个括号里名字,更加简洁*/
typedef int (*ONDRAW_FUNC)(PButton ptButton, PDispBuff ptDispBuff);
typedef int (*ONPRESSED_FUNC)(PButton ptButton, PDispBuff ptDispBuff, PInputEvent ptInputEvent);typedef struct Button{char *name;int status;Region tRegion;ONDRAW_FUNC OnDraw;//绘图函数ONPRESSED_FUNC OnPressed;//按键状态改变后的处理函数
}Button, *PButton;void InitButton(PButton ptButton, char *name, PRegion ptRegion, ONDRAW_FUNC OnDraw, ONPRESSED_FUNC OnPressed);#endif

编写按钮的代码

        点击按钮后怎么处理,是业务系统的事情(顶层APP),所以应该提供一个 InitButton 函数,让用户可以提供 OnPressed 等底层函数,上层代码通过下面 3 个函数使用按钮。

button.c

#include <ui.h>/*默认的绘图函数*/
static int DefaultOnDraw(PButton ptButton, PDispBuff ptDispBuff)
{/*绘制默认底色*/DrawRegion(&ptButton->tRegion, BUTTON_DEFAULT_COLOR);/*在居中位置写文字*/DrawTextInRegionCentral(ptButton->name, &ptButton->tRegion, BUTTON_TEXT_COLOR);/*flush to lcd/web*/FlushDisplayRegion(&ptButton->tRegion, ptDispBuff);return 0;
}/*默认的按键状态变换时的处理函数*/
static int DefaultOnPressed(PButton ptButton, PDispBuff ptDispBuff, PInputEvent ptInputEvent)
{/*设置按键底色为默认的红色*/unsigned int dwColor = BUTTON_DEFAULT_COLOR;/*假设按键按下后,会调用这个函数,改变按键状态*/ptButton->status = !ptButton->status;if(ptButton->status)dwColor = BUTTON_PRESSED_COLOR;//改变按键底色为绿色/*绘制点击后的底色*/DrawRegion(&ptButton->tRegion, dwColor);/*在居中位置写文字*/DrawTextInRegionCentral(ptButton->name, &ptButton->tRegion, BUTTON_TEXT_COLOR);/*flush to lcd/web*/FlushDisplayRegion(&ptButton->tRegion, ptDispBuff);return 0;
}/*上层代码调用Init函数来进行初始化操作*/
void InitButton(PButton ptButton, char *name, PRegion ptRegion, ONDRAW_FUNC OnDraw, ONPRESSED_FUNC OnPressed)
{/*按键状态默认是松开*/ptButton->status    = 0;/*保存文本字符串*/ptButton->name      = name;*/保存按键的区域信息*/ptButton->tRegion   = *ptRegion;/*如果没有传入函数指针,就使用两个默认的处理函数*/ptButton->OnDraw    = OnDraw ? OnDraw : DefaultOnDraw;ptButton->OnPressed = OnPressed ? OnPressed : DefaultOnPressed;
}

        在 button.c 里,DrawRegion 函数和 DrawTextInRegionCentral 函数我们是在 disp_manager.c 里实现的,里面实现了一系列的绘制函数。

DrawRegion

void DrawRegion(PRegion ptRegion, unsigned int dwColor)
{/*得到区域的左上角坐标和宽度高度*/int x = ptRegion->iLeftUpX;int y = ptRegion->iLeftUpY;int width = ptRegion->iWidth;int heigh = ptRegion->iHeigh;int i,j;/*双层循环,逐个绘制像素就行了*/for(j = y; j < y + heigh; j++){for(i = x; i < x + width; i++)PutPixel(i, j, dwColor);}
}

DrawTextInRegionCentral

        如何在区域的居中位置显示文本?

void DrawTextInRegionCentral(char *name, PRegion ptRegion, unsigned int dwColor)
{// 计算文本字符串的长度int n = strlen(name);// 计算每个字符的宽度,以便文本能够居中显示int iFontSize = ptRegion->iWidth / n / 2;FontBitMap tFontBitMap; // 字体位图结构体int iOriginX, iOriginY; // 字符绘制的起始坐标int i = 0; // 用于遍历字符串的索引int error; // 用于存储错误代码// 如果计算出的字体大小超过了区域的高度,则将字体大小设置为区域的高度if (iFontSize > ptRegion->iHeigh)iFontSize =  ptRegion->iHeigh;// 计算文本在区域内的起始X坐标,确保文本居中iOriginX = (ptRegion->iWidth - n * iFontSize)/2 + ptRegion->iLeftUpX;// 计算文本在区域内的起始Y坐标,确保文本居中iOriginY = (ptRegion->iHeigh - iFontSize)/2 + iFontSize + ptRegion->iLeftUpY;// 设置字体大小SetFontSize(iFontSize);// 遍历文本字符串中的每个字符while (name[i]){// 获取当前字符的字体位图tFontBitMap.iCurOriginX = iOriginX;tFontBitMap.iCurOriginY = iOriginY;error = GetFontBitMap(name[i], &tFontBitMap);if (error){printf("SelectAndInitFont err\n");return;}// 在缓冲区上绘制当前字符的字体位图DrawFontBitMap(&tFontBitMap, dwColor);// 更新下一个字符的起始坐标iOriginX = tFontBitMap.iNextOriginX;iOriginY = tFontBitMap.iNextOriginY;i++;}
}

单元测试

        编写单元测试代码 ui_test.c

ui_test.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>#include <ui.h>int main(int argc, char **argv)
{// 定义显示缓冲区和错误代码的指针,以及按钮和区域的结构体PDispBuff ptBuffer;int error;Button tButton;Region tRegion;if (argc != 2){//传入的参数个数不对就打印用法,需要传入字体文件printf("Usage: %s <font_size>\n", argv[0]);return -1;}// 初始化显示DisplayInit();// 选择默认的显示操作接口,这里假设"fb"是一个帧缓冲设备SelectDefaultDisplay("fb");// 初始化默认的显示操作接口InitDefaultDisplay();// 获取显示缓冲区ptBuffer = GetDisplayBuffer();// 注册字体FontsRegister();// 选择并初始化字体,这里使用的是"freetype"字体,字体大小由命令行参数指定error = SelectAndInitFont("freetype", argv[1]);if (error){printf("SelectAndInitFont err\n");return -1;}// 初始化区域的坐标和大小tRegion.iLeftUpX = 200;tRegion.iLeftUpY = 200;tRegion.iWidth   = 300;tRegion.iHeigh   = 100;// 初始化按钮,设置按钮的文本和区域,以及回调函数InitButton(&tButton, "test", &tRegion, NULL, NULL);// 绘制按钮tButton.OnDraw(&tButton, ptBuffer);//用软件模拟按键按下,周期为2swhile (1){tButton.OnPressed(&tButton, ptBuffer, NULL);// 暂停2秒sleep(2);}// 程序正常结束return 0;	
}

unittest下的Makefile

        文件结构如下:

EXTRA_CFLAGS  := 
CFLAGS_file.o := 
 
#obj-y += disp_test.o
#obj-y += input_test.o
#obj-y += font_test.o
obj-y += ui_test.o

        最后一行表示要编译 ui_test.c 

ui目录下的Makefile

EXTRA_CFLAGS  := 
CFLAGS_file.o := 
 
obj-y += button.o

        编译 button.c 

顶层目录下的Makefile

# 定义交叉编译工具链的前缀,这里留空,意味着使用系统默认的编译器
CROSS_COMPILE ?= 
 
# 定义汇编器、链接器、C编译器、预处理器、归档器和符号表工具
AS        = $(CROSS_COMPILE)as
LD        = $(CROSS_COMPILE)ld
CC        = $(CROSS_COMPILE)gcc
CPP        = $(CC) -E
AR        = $(CROSS_COMPILE)ar
NM        = $(CROSS_COMPILE)nm
 
# 定义二进制文件处理工具
STRIP        = $(CROSS_COMPILE)strip
OBJCOPY        = $(CROSS_COMPILE)objcopy
OBJDUMP        = $(CROSS_COMPILE)objdump
 
# 导出定义的编译工具
export AS LD CC CPP AR NM
export STRIP OBJCOPY OBJDUMP
 
# 定义C编译器的标志
CFLAGS := -Wall -O2 -g
CFLAGS += -I $(shell pwd)/include # 添加头文件搜索路径
 
# 定义链接器的标志
LDFLAGS := -lts -lpthread -lfreetype # 链接时需要的库
 
# 导出编译和链接标志
export CFLAGS LDFLAGS
 
# 定义项目顶层目录
TOPDIR := $(shell pwd)
export TOPDIR
 
# 定义目标程序名
TARGET := test
 
# 定义源代码目录结构,每个目录下的文件将被编译成对应的.o文件
obj-y += display/
obj-y += input/
obj-y += font/
obj-y += ui/
obj-y += unittest/

 
# 默认目标,编译所有内容
all : start_recursive_build $(TARGET)
    @echo $(TARGET) has been built!
 
# 开始递归构建,使用当前目录下的Makefile.build文件
start_recursive_build:
    make -C ./ -f $(TOPDIR)/Makefile.build
 
# 定义目标程序的构建规则
$(TARGET) : built-in.o
    $(CC) -o $(TARGET) built-in.o $(LDFLAGS)
 
# 清理目标,删除所有生成的.o文件和目标程序
clean:
    rm -f $(shell find -name "*.o")
    rm -f $(TARGET)
 
# 深度清理目标,删除所有生成的.o文件、依赖文件和目标程序
distclean:
    rm -f $(shell find -name "*.o")
    rm -f $(shell find -name "*.d")
    rm -f $(TARGET)

        加粗部分表示要进入这些目录下编译 .c 文件 

运行结果

        板子挂载 Ubuntu 的 NFS 目录,编译运行:

        运行结果如下 :


        至此UI系统就完成了,下节我会更新量产工具的第五篇——页面系统,距离完成这个项目越来越近了,希望大家多多点赞支持。


http://www.ppmy.cn/embedded/139524.html

相关文章

【大数据学习 | flume】flume Sink Processors与拦截器Interceptor

1. Failover Sink Processor 故障转移处理器可以同时指定多个sink输出&#xff0c;按照优先级高低进行数据的分发&#xff0c;并具有故障转移能力。 需要修改第一台服务器agent a1.sourcesr1 a1.sinksk1 k2 a1.channelsc1 a1.sources.r1.typenetcat a1.sources.r1.bindworker…

word转pdf保存高清图技巧

1. PPT编辑的图片转矢量图&#xff0c;导入word 2.PPT的图全选复制到visio&#xff0c;再从visio复制到word 3.使用adboe PDF word插件&#xff0c;另存为ADOBE PDF。 图片保存质量 另存为DPF< 导入PDF < 另存为ADOBE PDF 但是ADOBE PDF有时候图片保存出来会一片空白…

uni-app获取安全区域

2024年8月2日 使用自定义导航栏的uni-app项目在真机演示时&#xff0c;可能会出现页面内容被手机的刘海或者状态栏给遮挡了&#xff0c;或者在小程序上给胶囊按钮给档住了。这时候就需要获取刘海屏状态了和胶囊按钮的高度来获取安全渲染区域. 1. CSS内置变量 来自uni-aap官网&…

32.2 prometheus倒排索引统计功能

本节重点介绍 : 获取采集端的高基数metrics的tsdb页面解析tsdb统计函数Stats源码解读 依赖倒排索引统计 获取采集端的高基数metrics tsdb页面解析 Top 10 label names with value count&#xff1a; 标签中value最多的10个Top 10 series count by metric names&#xff1a…

如何提高代理IP的并发能力

在数据采集的领域&#xff0c;时间就是金钱&#xff0c;效率就是生命。代理IP的并发能力直接关系到我们能否快速、高效地获取数据。那么&#xff0c;如何提高代理IP的并发能力呢&#xff1f;今天&#xff0c;我们就来聊聊这个话题&#xff0c;为你的数据采集项目提速。 并发&a…

Vue项目开发 element-UI 前端实现 1到10排列选择的按钮

在 Element UI 中&#xff0c;你可以通过 el-button 来实现按钮的排列选择&#xff0c;例如让用户选择 1 到 10 之间的数字。为了实现这一功能&#xff0c;我们可以使用 v-for 来动态生成 1 到 10 的按钮&#xff0c;并通过按钮点击事件来更新所选的数字。 以下是一个基本的实…

【高等数学学习记录】隐函数及由参数方程所确定的函数的导数、相关变化率

一、知识点 &#xff08;一&#xff09;隐函数的导数 显函数 对于形如 y s i n x ysinx ysinx 这种等号左端是因变量&#xff0c;右端是含有自变量的式子&#xff0c;当自变量取定义域内任一值时&#xff0c;由这个式子能确定对应的函数值&#xff0c;这种方式表达的函数叫做…

FPGA上板项目(六)——UART测试,串口收发

目录 实验内容串口接收模块模块框图时序波形仿真结果 顶层模块设计模块框图时序波形代码调整仿真结果上板测试 实验内容 将接收到的数据发送出去&#xff0c;实现串口回环。 串口发送的内容 FPGA上板项目&#xff08;五&#xff09;——UART测试&#xff0c;串口发送 已做过阐…