如何利用Guider快速入门LVGL(ESP32+arduino)

本文章适合入门的萌新

 最近在和同学做一个开源项目,我负责了项目的UI设计,在我一开始了解LVGL库的时候还是有被吓到的,因为我没有相关的开发经验,我认为要入门这个库是需要挺长时间的。但是之后经过我曲折的学习,我发现利用AI还有LVGL辅助工具(Guider)还是可以很快入门的。、

这个是项目地址【星火计划】人脸识别指纹考勤机 - 立创开源硬件平台

下面是基础知识储备,也是学习的前提:
        学会TFT屏幕的基础配置,并会根据库里的示例进行快速应用和移植。比如我用的是ST7796S + XPT2046 ,那我得去寻找相关的库,对其进行快速的使用(如TFT_eSPI库可以用于大多数屏幕芯片,和触摸芯片,但是我的触摸芯片驱动不在里面,所以我又自己去找了个库,根据代码直接移植)。

一、Guider的学习

        关于这个软件,网上的资料特别多,但是少了关于代码部分的讲解,所以这里我主要对其生成的代码进行讲解,我会将其他相关的资料贴出来,以便大家学习。

1 LVGL的基本认识和Guider的使用

1.1 LVGL各部件的介绍:

LVGL常用部件-优快云博客(如果对LVGL有相关了解,可以不看)

1.2 Guider基本操作示例:

经过以上一顿操作,我们掌握了基本的操作,并且可以及时的体验一下学习成果

2 代码移植

Guider的使用和代码移植(这个比较长,可以参考一下下面的文章):Arduino应用开发——使用GUI-Guider制作LVGL UI并导入ESP32运行_gui guider教程-优快云博客

3 Guider生成代码解读

软件生成的一坨代码,我们第一次看到的时候应该是比较害怕的,但是我们移植的主要的文件只有“custom”和“generated”(假设我们已经移植好了LVGL并配置了屏幕触屏芯片的驱动)

而这两个文件中的custom其实是在Guider使用时不会被改变的文件,也就是我们可以写代码的地方,但是在开发中也是很少用到的(就像HAL库开发中我们自己添加代码的地方,在后续图形化配置中,这个地方不会被软件修改)

OK了解了以上后就只需要知道Generated文件里面是什么东东就基本搞定了生成代码的结构(下面以我自己写的一个比较多内容的UI为例,在最上面有链接)

可见,文件内的东西几乎包括了我们在Guider中的每一步操作(添加屏幕、部件、事件、图片、字体,这些都在文件内,也就是说我们可以根据这些实现我们自己的功能)

4 代码执行步骤(除去LVGL/屏幕/触摸芯片的初始化)

在setup中我们也可以发现上面generated的初始化基本都集中在一起,Guider先对自己特有的代码、事件代码、本地代码进行了初始化。以下面代码为例,我们利用ai了解一下“init_scr_del_flag“这个函数有什么作用

从上面代码我们知道,最开始初始化了第一个页面"screen_first"

下面我们以screen_first为例子进行讲解,我们跳转到screen_first页面所在的文件

在其中我们看见各种我们在Guider中放置的部件,以screen_first_img_background为例子其中‘screen_first’是我第一页页面的命名,‘img’表示的是图形的意思,‘background’是我导入的背景图像的命名,可见guider生成的代码特别容易看懂。同个部件的配置放在一起,简洁明了。

我们可以用AI解读一下,我们也可以修改参数,看看现实效果(比如改变透明度,或者性质等等,用AI可以快速知道某种效果是如何实现的)

// 在 screen_first 容器上创建背景图片对象
ui->screen_first_img_background = lv_img_create(ui->screen_first);

// 为图片对象添加可点击标志(使其能响应点击事件)
lv_obj_add_flag(ui->screen_first_img_background, LV_OBJ_FLAG_CLICKABLE);

// 设置图片来源(使用预定义的 _BACKGROUND_alpha_480x320 图像资源)
lv_img_set_src(ui->screen_first_img_background, &_BACKGROUND_alpha_480x320);

// 设置图片旋转中心点坐标(X:50, Y:50)
lv_img_set_pivot(ui->screen_first_img_background, 50, 50);

// 设置图片旋转角度(0 度表示不旋转)
lv_img_set_angle(ui->screen_first_img_background, 0);

// 设置图片在父容器中的位置(左上角坐标 X:0, Y:0)
lv_obj_set_pos(ui->screen_first_img_background, 0, 0);

// 设置图片尺寸(宽 480 像素,高 320 像素)
lv_obj_set_size(ui->screen_first_img_background, 480, 320);

/*-- 设置图片样式(针对主部件,默认状态)--*/
// 设置图片颜色混合透明度为 0(禁用颜色混合效果)
lv_obj_set_style_img_recolor_opa(ui->screen_first_img_background, 0, LV_PART_MAIN|LV_STATE_DEFAULT);

// 设置图片整体不透明度为 255(完全不透明)
lv_obj_set_style_img_opa(ui->screen_first_img_background, 255, LV_PART_MAIN|LV_STATE_DEFAULT);

// 设置边框圆角半径为 0(直角矩形)
lv_obj_set_style_radius(ui->screen_first_img_background, 0, LV_PART_MAIN|LV_STATE_DEFAULT);

// 启用角落裁剪(当子对象超出边界时会被裁剪)
lv_obj_set_style_clip_corner(ui->screen_first_img_background, true, LV_PART_MAIN|LV_STATE_DEFAULT);

下面我们来看一下事件的触发是如何实现的,滑倒代码的最后,我们可以看到两句代码,其中一句就是用来初始化事件的。

来到页面事件初始函数中,我们看见有两句代码,每一句代码都指向了一个部件,还有一个回调函数。

让我们看一下回调函数里面有什么

回调函数主要就是获取当前部件的状态(比如点击、或者长按),判断状态是否可以进入事件。

比如上面的代码的意思就是在screen_first页面中,如果daka按钮这个部件检测到CLICKED按下状态,就会触发animation动画,并跳转到screen_sign页面。下面就进行页面初始化......

可以看见代码逻辑还是很清晰的。

二、利用现有基础撰写LVGL代码

例子:输入账号密码,验证后可进入下一页

在本项目中,我们需要联网,并且输入正确的账号密码,进行管理员操作,简化一下就如标题所说。在Guider中是无法完成这一个复杂操作的,所以需要我们手动修改代码。

看一下部件与界面的名字,好进一步了解代码

要完成上面的过程,其实我们只需要添加在点击“确认”时,我们触发一下“事件”,触发时,将“两个输入框”内的内容发送到服务器,在接收服务器反馈,如果账号密码准确,那么“跳转到下一个界面”。

所以我们只需要写一个事件,事件的代码如下

我写了一个函数,用来判断中间变量,然后进入相关的函数

而Loading_event = EnterAdministrator时,我写的代码会执行下面这一部分(我对重要的LVGL代码进行了注释)

else if(Loading_event == EnterAdministrator)//ID密码验证,进入管理员系统
    {
      Internet_GetStatus();
      if (Internet_Status || Wired_Network_Flag)//有网络,对ID密码进行验证
      {
            //获取输入框的内容
          const char *Password = lv_textarea_get_text(guider_ui.screen_set_administrator_password_ta_password);//获取密码
          const char *ID = lv_textarea_get_text(guider_ui.screen_set_administrator_password_ta_ID);//获取ID
          if(HTTP_Login(ID, Password)) //如果ID 密码正确,进入注册界面
          {
            //close_loading_screen();
            setup_scr_screen_loading(&guider_ui , current_screen, "RequsetSucessful" );  
            for(uint8_t i = 0 ;i <= 100 ; i++)
            {
              lv_timer_handler(); /* let the GUI do its work */
              delay(10);
            }
            //关闭加载界面,这个是我自己写的一个界面,运行时,
            //屏幕会显示Loading,也是模仿Guider的风格写的
            close_loading_screen();
            //进入注册界面(触发动画,然后切换画面)
            ui_load_scr_animation(&guider_ui, &guider_ui.screen_set_administrator, guider_ui.screen_set_administrator_del, &guider_ui.screen_set_administrator_del, setup_scr_screen_set_administrator, LV_SCR_LOAD_ANIM_NONE, 200, 200, false, true);
          }
          else
          {
            //close_loading_screen();
            setup_scr_screen_loading(&guider_ui , guider_ui.screen_set_administrator_password, "RequsetFailed" );  
            for(uint8_t i = 0 ;i <= 100 ; i++)
            {
              lv_timer_handler(); /* let the GUI do its work */
              delay(10);
            }
            close_loading_screen();
          }
      }
      else//没有网络
      {
        //close_loading_screen();
        setup_scr_screen_loading(&guider_ui , guider_ui.screen_set_administrator_password, "NoInternet" );  
        for(uint8_t i = 0 ;i <= 100 ; i++)
        {
          lv_timer_handler(); /* let the GUI do its work */
          delay(10);
        }
        close_loading_screen();
      }

最重要的其实也就是ui_load_scr_animation(&guider_ui, &guider_ui.screen_set_administrator, guider_ui.screen_set_administrator_del, &guider_ui.screen_set_administrator_del, setup_scr_screen_set_administrator, LV_SCR_LOAD_ANIM_NONE, 200, 200, false, true);这一句事件触发,只是根据自己的需求添加了一些额外的步骤

下面是我写的loading界面代码,是模仿Guider风格的


//显示特定的文字,information为显示的信息
void setup_scr_screen_loading(lv_ui *ui , lv_obj_t *page,  const char* information)
{

	ui->screen_loading_label_1 = lv_label_create(page);

	lv_label_set_text(ui->screen_loading_label_1, information);

	lv_label_set_long_mode(ui->screen_loading_label_1, LV_LABEL_LONG_WRAP);
	lv_obj_set_pos(ui->screen_loading_label_1, -29, -13);
	lv_obj_set_size(ui->screen_loading_label_1, 533, 357);
	lv_obj_add_flag(ui->screen_loading_label_1, LV_OBJ_FLAG_CLICKABLE );

	//Write style for screen_loading_label_1, Part: LV_PART_MAIN, State: LV_STATE_DEFAULT.
	lv_obj_set_style_border_width(ui->screen_loading_label_1, 0, LV_PART_MAIN|LV_STATE_DEFAULT);
	lv_obj_set_style_radius(ui->screen_loading_label_1, 0, LV_PART_MAIN|LV_STATE_DEFAULT);
	lv_obj_set_style_text_color(ui->screen_loading_label_1, lv_color_hex(0xf21717), LV_PART_MAIN|LV_STATE_DEFAULT);
	lv_obj_set_style_text_font(ui->screen_loading_label_1, &myFont25, LV_PART_MAIN|LV_STATE_DEFAULT);
	lv_obj_set_style_text_opa(ui->screen_loading_label_1, 255, LV_PART_MAIN|LV_STATE_DEFAULT);
	lv_obj_set_style_text_letter_space(ui->screen_loading_label_1, 2, LV_PART_MAIN|LV_STATE_DEFAULT);
	lv_obj_set_style_text_line_space(ui->screen_loading_label_1, 0, LV_PART_MAIN|LV_STATE_DEFAULT);
	lv_obj_set_style_text_align(ui->screen_loading_label_1, LV_TEXT_ALIGN_CENTER, LV_PART_MAIN|LV_STATE_DEFAULT);
	lv_obj_set_style_bg_opa(ui->screen_loading_label_1, 0, LV_PART_MAIN|LV_STATE_DEFAULT);
	lv_obj_set_style_bg_color(ui->screen_loading_label_1, lv_color_hex(0x2387db), LV_PART_MAIN|LV_STATE_DEFAULT);
	lv_obj_set_style_bg_grad_dir(ui->screen_loading_label_1, LV_GRAD_DIR_NONE, LV_PART_MAIN|LV_STATE_DEFAULT);
	lv_obj_set_style_pad_top(ui->screen_loading_label_1, 116, LV_PART_MAIN|LV_STATE_DEFAULT);
	lv_obj_set_style_pad_right(ui->screen_loading_label_1, 0, LV_PART_MAIN|LV_STATE_DEFAULT);
	lv_obj_set_style_pad_bottom(ui->screen_loading_label_1, 0, LV_PART_MAIN|LV_STATE_DEFAULT);
	lv_obj_set_style_pad_left(ui->screen_loading_label_1, 0, LV_PART_MAIN|LV_STATE_DEFAULT);
	lv_obj_set_style_shadow_width(ui->screen_loading_label_1, 0, LV_PART_MAIN|LV_STATE_DEFAULT);

	//The custom code of screen_loading.
	
	//Update current screen layout.
	lv_obj_update_layout(page);
}


// 关闭加载页面
void close_loading_screen() 
{
	if(lv_obj_is_valid(guider_ui.screen_loading_label_1))
	{
		// lv_obj_clean(guider_ui.screen_loading_label_1);
		lv_obj_del(guider_ui.screen_loading_label_1);
		// 清空指针,避免悬挂指针
		guider_ui.screen_loading_label_1 = NULL;
	}
}

例子:WIFI扫描并显示在屏幕上,可以选择与输入密码,可连接

这个代码基本上是AI生成出来的,思路与上面一致

在页面加载中添加了WIFI名称的部件

if(WIFI_N > 0)
	{
		printf("have_wifi\n");
		//----------------------------删除上次生成的对象--------------------------
		for (uint8_t i = 0; i < 50; i++)
        {
            // if (ui->screen_Internet_list_wifi_item[i] != nullptr)
			//如果对象存在,则把对象清除
			if (lv_obj_is_valid(ui->screen_Internet_list_wifi_item[i]))
            {
				lv_obj_remove_style_all(ui->screen_Internet_list_wifi_item[i]);
                ui->screen_Internet_list_wifi_item[i] = nullptr; // 设置为nullptr防止悬空指针
            }
        }
		wifi_i = 0;

		//添加新的WFI对象
		for (const auto& info : sortedWiFiList)
		{
			String information = String(info.ssid) + " (" + String(info.rssi) + " dBm)" + String(info.encryptionType);
			ui->screen_Internet_list_wifi_item[wifi_i] = lv_list_add_btn(ui->screen_Internet_list_wifi, LV_SYMBOL_WIFI, information.c_str());
			lv_obj_add_style(ui->screen_Internet_list_wifi_item[wifi_i], &style_screen_Internet_list_wifi_extra_btns_main_default, LV_PART_MAIN | LV_STATE_DEFAULT);
			lv_obj_add_style(ui->screen_Internet_list_wifi_item[wifi_i], &style_screen_Internet_list_wifi_extra_btns_main_checked, LV_PART_MAIN | LV_STATE_CHECKED);
			wifi_i++;
			if (wifi_i >= 49)
			{
				return;
			}
					
		}
		wifi_i_last=wifi_i;
	}

另外的,在事件中,添加了选择与密码验证

下面是选择事件

    lv_obj_add_event_cb(ui->screen_Internet_btn_wifi, screen_Internet_btn_wifi_event_handler, LV_EVENT_ALL, ui);

    // 为 Wi-Fi 按钮添加点击事件处理:只能选中其中一个WIFI
    for (uint8_t i = 0; i < wifi_i + 1; i++) {
        if (lv_obj_is_valid(ui->screen_Internet_list_wifi_item[i])) {
            lv_obj_remove_event_cb(ui->screen_Internet_list_wifi_item[i],screen_Internet_item_wifi_event_handler);
            lv_obj_add_event_cb(ui->screen_Internet_list_wifi_item[i], screen_Internet_item_wifi_event_handler, LV_EVENT_CLICKED, ui);
        }
    }

下面是连接事件

else if(Loading_event == ConnectWIFI) //连接WIFI
    {
      close_loading_screen();
      printf( "\ngo to CONNECT" );
      //const char *Password = "88888888";//
      const char *Password = lv_textarea_get_text(guider_ui.screen_Internet_ta_password);//获取WIFI密码
      uint8_t WIFI_STATUS=Internet_ConnectWIFI(selectedSSID,Password);//返回1表示连接成功,返回0表示连接失败
      printf("\nPASSWORD%s",Password);//输出WIFI信息
      printf("\nID%s",selectedSSID);
      printf("\nNUM%d\n",WIFI_STATUS);
      //如果连接成功
      if(WIFI_STATUS)
      {
        Wired_Network_Flag = false;
        setup_scr_screen_loading(&guider_ui , guider_ui.screen_Internet, "SucessfulConncet" );
        HTTP_getTime();
        status_page_set_internet(http_hour,http_minute,current_screen);
      }
      //如果连接失败
      else
      {
        setup_scr_screen_loading(&guider_ui , guider_ui.screen_Internet, "DefeatConncet" );
      }
      for(uint8_t i = 0 ;i <= 100 ; i++)
      {
        lv_timer_handler(); /* let the GUI do its work */
        delay(10);
      }
      close_loading_screen();
      //Loading_event = ScanWIFI;         
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值