当前位置: 首页 > article >正文

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

前言

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

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

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

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

项目框架

        本节我们来实现量产工具的第四个子系统——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);

    //用软件模拟按键按下,周期为2s
	while (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.kler.cn/a/403714.html

相关文章:

  • 游戏陪玩系统开发功能需求分析
  • Transformer中的Self-Attention机制如何自然地适应于目标检测任务
  • java-贪心算法
  • vue3:使用插件递归组件
  • 【代码pycharm】动手学深度学习v2-04 数据操作 + 数据预处理
  • 探索Python编程:从入门到实践的高效指南
  • GPTZero:高效识别AI生成文本,保障学术诚信与内容原创性
  • leetcode-12-整数转罗马数字
  • 马铃薯叶部病害图像分类数据集
  • 【Swift】运算符
  • 小程序接入腾讯地图并使用(完整版带api示例)
  • 插入排序insertsort
  • YOLO入门教程(三)——训练自己YOLO11实例分割模型并预测【含教程源码+一键分类数据集 + 故障排查】
  • 通义千问综合代码能力测试:制作web版五子棋
  • MFC线程管理类
  • Python设计模式详解之4 —— 建造者模式
  • 【Anomaly Detection论文阅读记录】PaDiM与PatchCore模型的区别与联系
  • 内网渗透-隧道判断-SSH-DNS-icmp-smb-上线linux-mac
  • 网络安全问题概述
  • 三十一、构建完善微服务——API 网关
  • 初始Python篇(4)—— 元组、字典
  • 查找萤石云IOS Sdk中的编解码接口
  • 基于Lora通讯加STM32空气质量检测WIFI通讯-分享
  • Mistral 发布开源多模态模型 Pixtral Large,聊天助手 Le Chat 全面对标 ChatGPT
  • Palo Alto Networks PAN-OS身份认证绕过漏洞复现(CVE-2024-0012)
  • IM项目-----客户端部分未读消息实现