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

LVGL实现一个简易画图板

因为工作需要用LVGL实现了一个简易的画图板
LVGL版本:8.2

主要功能有画图,选择画笔颜色,设置画笔宽度,画图板清空

代码如下:

#ifndef __LV_OBJ_SKETCHPAD_H__
#define __LV_OBJ_SKETCHPAD_H__

#include "lvgl/lvgl.h"

#ifdef __cplusplus
extern "C" {
#endif

#define SKETCHPAD_DEFAULT_WIDTH   (800)
#define SKETCHPAD_DEFAULT_HEIGHT  (480)

typedef enum {
    LV_SKETCHPAD_TOOLBAR_OPT_ALL = 0,
    LV_SKETCHPAD_TOOLBAR_OPT_COOLWHEEL, // 色轮
    LV_SKETCHPAD_TOOLBAR_OPT_WIDTH, // 画笔宽度调节
    LV_SKETCHPAD_TOOLBAR_OPT_CLEAR, // 清空画布
    LV_SKETCHPAD_TOOLBAR_OPT_LAST
}lv_100ask_sketchpad_toolbar_e;

/*Data of canvas*/
typedef struct {
    lv_img_t img;
    lv_img_dsc_t dsc;
    lv_draw_line_dsc_t line_rect_dsc;
} lv_obj_sketchpad_t;

/***********************
    * GLOBAL VARIABLES
    ***********************/

    /**********************
    * GLOBAL PROTOTYPES
    **********************/
lv_obj_t* lv_obj_sketchpad_create(lv_obj_t* parent);

void lv_gui_sketchpad_display();


#ifdef __cplusplus
} /*extern "C"*/
#endif

#endif // __LV_GUI_SKETCHPAD_H__
#include "lv_obj_sketchpad.h"


/*********************
 *      DEFINES
 *********************/
#define THIS_FILE "lv_obj_sketchpad.c"
#define MY_CLASS &lv_obj_sketchpad_class


 /**********************
  *      TYPEDEFS
  **********************/


 /**********************
  *  STATIC PROTOTYPES
  **********************/
static void lv_obj_sketchpad_constructor(const lv_obj_class_t* class_p, lv_obj_t* obj);
static void lv_obj_sketchpad_destructor(const lv_obj_class_t* class_p, lv_obj_t* obj);

static void lv_sketchpad_toolbar_constructor(const lv_obj_class_t* class_p, lv_obj_t* obj);
static void lv_sketchpad_toolbar_destructor(const lv_obj_class_t* class_p, lv_obj_t* obj);

static void lv_obj_sketchpad_event(const lv_obj_class_t* class_p, lv_event_t* e);
static void lv_sketchpad_toolbar_event(const lv_obj_class_t* class_p, lv_event_t* e);

static void sketchpad_toolbar_event_cb(lv_event_t* e);
static void toolbar_set_event_cb(lv_event_t* e);

/**********************
 *  STATIC VARIABLES
 **********************/
const lv_obj_class_t lv_obj_sketchpad_class = {
    .constructor_cb = lv_obj_sketchpad_constructor,
    .destructor_cb = lv_obj_sketchpad_destructor,
    .event_cb = lv_obj_sketchpad_event,
    .width_def = LV_PCT(100), // 默认宽度
    .height_def = LV_PCT(100), // 默认高度
    .instance_size = sizeof(lv_obj_sketchpad_t),
    .base_class = &lv_canvas_class
};

const lv_obj_class_t lv_sketchpad_toolbar_class = {
    .constructor_cb = lv_sketchpad_toolbar_constructor,
    .destructor_cb = lv_sketchpad_toolbar_destructor,
    .event_cb = lv_sketchpad_toolbar_event,
    .width_def = LV_SIZE_CONTENT,
    .height_def = LV_SIZE_CONTENT,
    .base_class = &lv_obj_class
};

/**********************
 *      MACROS
 **********************/

 /**********************
  *   GLOBAL FUNCTIONS
  **********************/

lv_obj_t* lv_obj_sketchpad_create(lv_obj_t* parent)
{
    LV_LOG_INFO("begin");
    lv_obj_t* obj = lv_obj_class_create_obj(MY_CLASS, parent);
    lv_obj_class_init_obj(obj);
    return obj;
}


/*=====================
 * Other functions
 *====================*/

 /**********************
  *   STATIC FUNCTIONS
  **********************/

static void lv_obj_sketchpad_constructor(const lv_obj_class_t* class_p, lv_obj_t* obj)
{
    LV_UNUSED(class_p);
    LV_TRACE_OBJ_CREATE("begin");

    if (obj == NULL)
        return;

    lv_obj_sketchpad_t* sketchpad = (lv_obj_sketchpad_t *)obj;

    sketchpad->dsc.header.always_zero = 0;
    sketchpad->dsc.header.cf = LV_IMG_CF_TRUE_COLOR;
    sketchpad->dsc.header.h = 0;
    sketchpad->dsc.header.w = 0;
    sketchpad->dsc.data_size = 0;
    sketchpad->dsc.data = NULL;

    lv_draw_line_dsc_init(&sketchpad->line_rect_dsc);
    sketchpad->line_rect_dsc.width = 2;
    sketchpad->line_rect_dsc.round_start = true;
    sketchpad->line_rect_dsc.round_end = true;
    sketchpad->line_rect_dsc.color = lv_color_make(0xFF, 0x0, 0x00);
    sketchpad->line_rect_dsc.opa = LV_OPA_COVER;

    lv_img_set_src(obj, &sketchpad->dsc);

    lv_obj_add_flag(obj, LV_OBJ_FLAG_CLICKABLE); // 添加点击属性

    /*toolbar 基于sketchpad创建toolbar*/
    lv_obj_t* toolbar = lv_obj_class_create_obj(&lv_sketchpad_toolbar_class, obj);
    lv_obj_class_init_obj(toolbar);

    LV_TRACE_OBJ_CREATE("finished");
}

static void lv_obj_sketchpad_destructor(const lv_obj_class_t* class_p, lv_obj_t* obj)
{
    LV_UNUSED(class_p);
    LV_TRACE_OBJ_CREATE("begin");

    if (obj == NULL)
        return;

    lv_canvas_t* canvas = (lv_canvas_t*)obj;
    lv_img_cache_invalidate_src(&canvas->dsc);

    LV_TRACE_OBJ_CREATE("finished");
}


static void lv_obj_sketchpad_event(const lv_obj_class_t* class_p, lv_event_t* e)
{
    LV_UNUSED(class_p);

    /*Call the ancestor's event handler*/
    lv_res_t res = lv_obj_event_base(MY_CLASS, e);
    if (res != LV_RES_OK)
        return;

    lv_event_code_t code = lv_event_get_code(e);
    lv_obj_t* obj = lv_event_get_target(e);
    lv_obj_sketchpad_t* sketchpad = (lv_obj_sketchpad_t*)obj;

    static lv_coord_t last_x = 0, last_y = -32768;

    if (code == LV_EVENT_PRESSING)
    {
        lv_indev_t* indev = lv_indev_get_act();
        if (indev == NULL)  return;

        lv_point_t point;
        lv_indev_get_point(indev, &point);

        lv_point_t points[2] = { 0 };

        /*Release or first use*/
        if ((last_x == -32768) || (last_y == -32768))
        {
            last_x = point.x;
            last_y = point.y;
        }
        else
        {
            points[0].x = last_x;
            points[0].y = last_y;
            points[1].x = point.x;
            points[1].y = point.y;
            last_x = point.x;
            last_y = point.y;

            lv_canvas_draw_line(obj, points, 2, &sketchpad->line_rect_dsc);
        }
    }
    /*Loosen the brush*/
    else if (code == LV_EVENT_RELEASED)
    {
        last_x = -32768;
        last_y = -32768;
    }
}


/*toolbar*/
static void lv_sketchpad_toolbar_constructor(const lv_obj_class_t* class_p, lv_obj_t* obj)
{
    LV_UNUSED(class_p);
    LV_TRACE_OBJ_CREATE("begin");

    lv_obj_add_flag(obj, LV_OBJ_FLAG_CLICKABLE); // 添加可点击属性
    lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE); // 移除可滚动属性
    
    // 设置流式布局
    lv_obj_set_flex_grow(obj, 1);
    lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_ROW);
    lv_obj_set_size(obj, 60, 20);
    lv_obj_align(obj, LV_ALIGN_TOP_MID, 0, 0);

    // 颜色选择
    static lv_coord_t sketchpad_toolbar_colorwheel = LV_SKETCHPAD_TOOLBAR_OPT_COOLWHEEL;
    lv_obj_t* color_edit = lv_label_create(obj);
    lv_label_set_text(color_edit, LV_SYMBOL_EDIT);
    lv_obj_add_flag(color_edit, LV_OBJ_FLAG_CLICKABLE);
    lv_obj_set_size(color_edit, 20, 20);
    lv_obj_add_event_cb(color_edit, sketchpad_toolbar_event_cb, LV_EVENT_ALL, (void*)&sketchpad_toolbar_colorwheel);

    // 画笔宽度
    static lv_coord_t sketchpad_toolbar_width = LV_SKETCHPAD_TOOLBAR_OPT_WIDTH;
    lv_obj_t* pen_size = lv_label_create(obj);
    lv_label_set_text(pen_size, LV_SYMBOL_EJECT);
    lv_obj_add_flag(pen_size, LV_OBJ_FLAG_CLICKABLE);
    lv_obj_set_size(pen_size, 20, 20);
    lv_obj_add_event_cb(pen_size, sketchpad_toolbar_event_cb, LV_EVENT_ALL, (void*)&sketchpad_toolbar_width);

    // 清空按钮
    static lv_coord_t sketchpad_toolbar_clear = LV_SKETCHPAD_TOOLBAR_OPT_CLEAR;
    lv_obj_t* clear_obj = lv_label_create(obj);
    lv_label_set_text(clear_obj, LV_SYMBOL_TRASH);
    lv_obj_add_flag(clear_obj, LV_OBJ_FLAG_CLICKABLE);
    lv_obj_set_size(clear_obj, 20, 20);
    lv_obj_add_event_cb(clear_obj, sketchpad_toolbar_event_cb, LV_EVENT_CLICKED, (void*)&sketchpad_toolbar_clear);

    LV_TRACE_OBJ_CREATE("finished");
}


static void lv_sketchpad_toolbar_destructor(const lv_obj_class_t* class_p, lv_obj_t* obj)
{

}


static void lv_sketchpad_toolbar_event(const lv_obj_class_t* class_p, lv_event_t* e)
{
    LV_UNUSED(class_p);

    /*Call the ancestor's event handler*/
    lv_res_t res = lv_obj_event_base(MY_CLASS, e);
    if (res != LV_RES_OK)
        return;

    lv_event_code_t code = lv_event_get_code(e);
    lv_obj_t* obj = lv_event_get_target(e);

    if (code == LV_EVENT_PRESSING)
    {
        lv_indev_t* indev = lv_indev_get_act();
        if (indev == NULL)
            return;

        lv_point_t vect;
        lv_indev_get_vect(indev, &vect);

        lv_coord_t x = lv_obj_get_x(obj) + vect.x;
        lv_coord_t y = lv_obj_get_y(obj) + vect.y;
        lv_obj_set_pos(obj, x, y);
    }
}

static void sketchpad_toolbar_event_cb(lv_event_t* e)
{
    lv_coord_t* toolbar_opt = (lv_coord_t *)lv_event_get_user_data(e);
    lv_event_code_t code = lv_event_get_code(e);
    lv_obj_t* obj = lv_event_get_target(e);
    lv_obj_t* toolbar = lv_obj_get_parent(obj);
    lv_obj_t* sketchpad = lv_obj_get_parent(toolbar);
    lv_obj_sketchpad_t* sketchpad_obj = (lv_obj_sketchpad_t*)sketchpad;

    if (code == LV_EVENT_CLICKED)
    {
        if ((*toolbar_opt) == LV_SKETCHPAD_TOOLBAR_OPT_COOLWHEEL)
        {
            static lv_coord_t sketchpad_toolbar_cw = LV_SKETCHPAD_TOOLBAR_OPT_COOLWHEEL;
            lv_obj_t* colorwheel = lv_colorwheel_create(sketchpad, true);
            lv_obj_align_to(colorwheel, obj, LV_ALIGN_OUT_BOTTOM_MID, 0, 0);
            lv_obj_add_event_cb(colorwheel, toolbar_set_event_cb, LV_EVENT_RELEASED, &sketchpad_toolbar_cw);
        }
        else if ((*toolbar_opt) == LV_SKETCHPAD_TOOLBAR_OPT_WIDTH)
        {
            static lv_coord_t sketchpad_toolbar_width = LV_SKETCHPAD_TOOLBAR_OPT_WIDTH;
            lv_obj_t* slider = lv_slider_create(sketchpad);
            lv_slider_set_value(slider, (int32_t)(sketchpad_obj->line_rect_dsc.width), LV_ANIM_OFF);
            lv_obj_align_to(slider, obj, LV_ALIGN_OUT_BOTTOM_MID, 0, 0);
            lv_obj_add_event_cb(slider, toolbar_set_event_cb, LV_EVENT_ALL, &sketchpad_toolbar_width);
        }
        else if ((*toolbar_opt) == LV_SKETCHPAD_TOOLBAR_OPT_CLEAR)
        {
            //lv_canvas_fill_bg(sketchpad, lv_palette_lighten(LV_PALETTE_GREY, 3), LV_OPA_COVER);
            lv_canvas_fill_bg(sketchpad, lv_color_make(0xFF, 0xFF, 0xFF), LV_OPA_COVER);
        }
    }
}

static void toolbar_set_event_cb(lv_event_t* e)
{
    lv_coord_t* toolbar_opt = (lv_coord_t *)lv_event_get_user_data(e);
    lv_event_code_t code = lv_event_get_code(e);
    lv_obj_t* obj = lv_event_get_target(e);
    lv_obj_sketchpad_t* sketchpad_obj = (lv_obj_sketchpad_t*)lv_obj_get_parent(obj);

    if (code == LV_EVENT_RELEASED)
    {
        if ((*toolbar_opt) == LV_SKETCHPAD_TOOLBAR_OPT_COOLWHEEL)
        {
            sketchpad_obj->line_rect_dsc.color = lv_colorwheel_get_rgb(obj);
            lv_obj_del(obj);
        }
        else if (*(toolbar_opt) == LV_SKETCHPAD_TOOLBAR_OPT_WIDTH)
        {
            lv_obj_del(obj);
        }
    }
    else if (code == LV_EVENT_VALUE_CHANGED)
    {
        if ((*toolbar_opt) == LV_SKETCHPAD_TOOLBAR_OPT_WIDTH)
        {
            sketchpad_obj->line_rect_dsc.width = (lv_coord_t)lv_slider_get_value(obj);
        }
    }
}


void lv_gui_sketchpad_display()
{
    static lv_color_t color_buf[LV_CANVAS_BUF_SIZE_TRUE_COLOR(SKETCHPAD_DEFAULT_WIDTH, SKETCHPAD_DEFAULT_HEIGHT)] = { 0 };

    lv_obj_t* sketchpad = lv_obj_sketchpad_create(lv_scr_act());
    if (sketchpad != NULL)
    {
        lv_canvas_set_buffer(sketchpad, color_buf, SKETCHPAD_DEFAULT_WIDTH, SKETCHPAD_DEFAULT_HEIGHT, LV_IMG_CF_TRUE_COLOR);
        //lv_obj_set_size(sketchpad, SKETCHPAD_DEFAULT_WIDTH, SKETCHPAD_DEFAULT_HEIGHT);
        lv_obj_center(sketchpad);
        //lv_canvas_fill_bg(sketchpad, lv_palette_lighten(LV_PALETTE_GREY, 3), LV_OPA_COVER);
        lv_canvas_fill_bg(sketchpad, lv_color_make(0xFF,0xFF,0xFF), LV_OPA_COVER);  
    }
}

运行效果:

参考文档:

lv_100ask_sketchpad

http://www.dtcms.com/a/108503.html

相关文章:

  • OSPF的Broadcast网络类型
  • [论文笔记] Deepseek技术报告解读: MLAMTP
  • Ubuntu与本地用户交流是两种小方法
  • 一个c#的简单日志记录类,避免使用Nuget依赖
  • Zabbix_agent自动注册教程!
  • 利用 PHP 爬虫按关键字搜索淘宝商品
  • uni-app 微信小程序 WebSocket 接入讯飞语音合成(流式版)WebAPI 示例
  • 【Redis】如何处理缓存穿透、击穿、雪崩
  • 在 Blazor 中使用 Chart.js 快速创建数据可视化图表
  • 大模型生成浏览器端的初级俄罗斯方块
  • 美团民宿 mtgsig 小程序 mtgsig1.2 分析
  • Unity中的C#脚本中文字符无法正常显示问题
  • mysql-创建和删除索引的两种方式
  • 针对 MySQL 数据库中 主键/唯一约束的更新方法 和 ON DUPLICATE KEY UPDATE 语法的详细说明及示例,并以表格总结
  • YOLOv11区域检测
  • Element Plus 常用组件
  • 超越人工智能驱动的网络攻击:现代防御者的策略
  • 电子电气架构 --- 智能座舱域控设计
  • Flutter 音视频播放器与弹幕系统开发实践
  • RK3588使用笔记:USB转232、485、422模块驱动适配
  • [蓝桥杯 2024 省 A] 训练士兵
  • 虚拟试衣间-云尚衣橱小程序-衣橱管理实现
  • 算法:二进制求和
  • 从零构建大语言模型全栈开发指南:第四部分:工程实践与部署-4.3.3低代码开发:快速构建行业应用(电商推荐与金融风控案例)
  • Python 驱动周立功200U收发报文
  • 国产系统服务器识别不到SATA盘
  • STM32学习笔记之RCC模块(实操篇)
  • 横扫SQL面试——中位数问题
  • git tag
  • Linux 中CIFS和NFS使用说明