使用GUI Guider工具开发嵌入式GUI应用(6)-切换多screen换场景

news2025/3/1 11:07:25

使用GUI Guider工具开发嵌入式GUI应用(6)-切换多screen换场景

本节将展示使用GUI Guider实现切换显示页面功能。

这里设计的用例是:

  • 创建3张页面,screen_0,screen_1screen_2
  • 分别在每个页面上中放置一个Label(最简单的显示组件),分别填入不同的字符串,例如WelcomeYOUHello等。
  • 为每个页面创建触发事件,配置GUI Guider让3个页面循环切换。当上一个页面载入完成后,切换页面开始载入下一个页面,循环往复。

在这里插入图片描述

图x 在GUI Guider中创建多页面

在GUI Guider编辑区的左上角,有页面管理窗口,鼠标单击对应的加号"+"按钮,就可以新建页面。如果要改新建页面的名字,不能在页面管理窗口中直接重命名,此时可在GUI Guider编辑区右侧的“属性设置”标签页改变“名字”中的内容。为了减少生成不必要的键盘组件,记得要把“是否显示键盘”的选项关闭。如图x所示。

在这里插入图片描述

图x 为新建页面改名

然后,在“事件添加”标签页中,创建一个事件:

  • 指定触发方式为“Loaded”,对应当载入本页面中所有的显示内容后,触发该事件
  • 指定目标对象为“screen_1”,对应当该事件发生后,对页面screen_1进行操作
  • 指定动作为“加载页面”,及加载screen_1的页面。这里还可以选择加载页面的速度为1000ms内启动加载,以及加载新的页面后停留多久1000ms,甚至还可以选择加载的特效为“move left”,即从右向左移入。

在这里插入图片描述

图x 为页面配置事件

对新建的3个页面都是如此配置,即screen_0的载入完成后加载screen_1,screen_1加载完成后加载screen_2,screen_2加载完成后加载screen_0,形成循环。之后,就可以生成代码并仿真了。

在实际调试到这里的时候,发现了当前版本GUI Guider的一个Bug:生成C代码后使用模拟器运行,切换到screen_1就卡住了,不会继续切换screen_2。但是使用MicroPython的模拟器运行配置好的GUI Guider工程就可以正常切换。经过对比源码以及调试过程后发现,生成C代码中,确定最终能产生触发信号的一个判定条件未被合理使用,该排判定条件始终不能被满足,因此无法正确执行到预设的触发事件的语句。但在Python代码中,就避开了这个判断,从而能够正常切换页面。一个临时的解法,是人工改动GUI Guider生成events_init.c文件源码,将其中的d->prev_src == NULL这个判断条件屏蔽掉。见源码如下:

static void screen_0_event_handler(lv_event_t *e)
{
	lv_event_code_t code = lv_event_get_code(e);
	switch (code)
	{
	case LV_EVENT_SCREEN_LOADED:
	{
		lv_obj_t * act_scr = lv_scr_act();
		lv_disp_t * d = lv_obj_get_disp(act_scr);
		//if (d->prev_scr == NULL && (d->scr_to_load == NULL || d->scr_to_load == act_scr))
         if (d->prev_scr == NULL || (d->scr_to_load == NULL || d->scr_to_load == act_scr))
		{
			if (guider_ui.screen_1_del == true)
				setup_scr_screen_1(&guider_ui);
			lv_scr_load_anim(guider_ui.screen_1, LV_SCR_LOAD_ANIM_MOVE_LEFT, 2500, 500, false);
			guider_ui.screen_0_del = false;
		}
	}
		break;
	default:
		break;
	}
}

void events_init_screen_0(lv_ui *ui)
{
	lv_obj_add_event_cb(ui->screen_0, screen_0_event_handler, LV_EVENT_ALL, ui);
}

static void screen_1_event_handler(lv_event_t *e)
{
	lv_event_code_t code = lv_event_get_code(e);
	switch (code)
	{
	case LV_EVENT_SCREEN_LOADED:
	{
		lv_obj_t * act_scr = lv_scr_act();
		lv_disp_t * d = lv_obj_get_disp(act_scr);
         //if (d->prev_scr == NULL && (d->scr_to_load == NULL || d->scr_to_load == act_scr))
		if (d->prev_scr == NULL || (d->scr_to_load == NULL || d->scr_to_load == act_scr))
		{
			if (guider_ui.screen_2_del == true)
				setup_scr_screen_2(&guider_ui);
			lv_scr_load_anim(guider_ui.screen_2, LV_SCR_LOAD_ANIM_MOVE_LEFT, 2500, 500, false);
			guider_ui.screen_1_del = false;
		}
	}
		break;
	default:
		break;
	}
}

void events_init_screen_1(lv_ui *ui)
{
	lv_obj_add_event_cb(ui->screen_1, screen_1_event_handler, LV_EVENT_ALL, ui);
}

static void screen_2_event_handler(lv_event_t *e)
{
	lv_event_code_t code = lv_event_get_code(e);
	switch (code)
	{
	case LV_EVENT_SCREEN_LOADED:
	{
		lv_obj_t * act_scr = lv_scr_act();
		lv_disp_t * d = lv_obj_get_disp(act_scr);
         //if (d->prev_scr == NULL && (d->scr_to_load == NULL || d->scr_to_load == act_scr))
		if (d->prev_scr == NULL || (d->scr_to_load == NULL || d->scr_to_load == act_scr))
		{
			if (guider_ui.screen_0_del == true)
				setup_scr_screen_0(&guider_ui);
			lv_scr_load_anim(guider_ui.screen_0, LV_SCR_LOAD_ANIM_MOVE_LEFT, 2500, 500, false);
			guider_ui.screen_2_del = false;
		}
	}
		break;
	default:
		break;
	}
}

void events_init_screen_2(lv_ui *ui)
{
	lv_obj_add_event_cb(ui->screen_2, screen_2_event_handler, LV_EVENT_ALL, ui);
}

从源码中可以看到,在检测screen_0对象事件的处理函数screen_0_event_handler()中,当检测到LV_EVENT_SCREEN_LOADED事件后,才有机会执行载入下一个页面的操作函数lv_scr_load_anim()。但在执行lv_scr_load_anim()函数之前,需要满足3个条件:

  • d->prev_scr == NULL表示前一页是否为没有
  • d->scr_to_load == NULL表示正在显示的页是否为没有
  • d->scr_to_load == act_scr 表示正在显示的页是否为当前页

源码中已经注释掉的判定条件要求必须没有上一页,这个判定条件仅有首页screen_0才能满足,到screen_1页面载入完成后,不能满足前一页面为没有的条件,因此无法继续执行载入screen_2页面的功能。一种直接的解法,就是屏蔽掉这个没有上页的判定条件,经仿真和上机调试后,验证可行。

最初设计这个没有前页的判定条件是有意义的。根据原作者的解释,此处的设计是为了避免在前一个页面中的事件未执行完毕时提前载入下一个页面,这可能在多线程环境下发挥更大的作用,但这需要LVGL内核实现一个机制:为当前页面中的所有事件挂一个信号量,只有当所有事件都执行完毕,所有的信号量都被归还后,由LVGL将当前页记录的上一页清空,从而满足此处设计的判定条件。不过,当前版本的LVGL内核并未实现这样的机制。所以为了能确保让当前的程序能够工作,还需要人为调整GUI Guider生成后的代码。特别注意,event_init.c这个文件是由GUI Guider自动生成的,每次生成代码都会覆盖,因此人为改动之后,如果再次执行自动生成代码的过程,仍需要再改一次。

调好代码之后,由于GUI Guider每次使用C程序的模拟器运行之前都会重新生成代码,覆盖人工调整源码,因此无法直接模拟正常程序的执行过程,但下载到单片机上,可以看到预期效果。如图x所示。

在这里插入图片描述

图x 在微控制器开发板上运行切换页面的程序

(未完待续。。。)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/896034.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

仿牛客论坛项目day7|Kafka

一、阻塞队列 创建了一个生产者线程和一个消费者线程。生产者线程向队列中放入元素,消费者线程从队列中取出元素。我们可以看到,当队列为空时,消费者线程会被阻塞,直到生产者线程向队列中放入新的元素。 二、Kafka入门 发布、订阅…

亿图脑图MindMaster思维导图及亿图图示会员-超值途径

亿图脑图MindMaster思维导图及亿图图示会员 先简单看一下这两软件: MindMaster 亿图图示 丰富的社区,便捷易操作的界面,还有耐看的UI设计;要是再有点特权,真的是锦上添花~ 如果需要MindMaster思维导图或者亿图图示VIP…

Android Retrofit原理浅析

官方地址:Retrofit 原理:Retrofit 本质上是代理了OKhttp,使用代理模式,Type-Safe 类型安全 编译器把类型检查出 避免类型错误, enqueue 异步 切换线程 execute 同步 不切换线程 enqueue:Call接口定义的抽象方法 Retrofit.Create() 方法首先验证接口validateServiceInterf…

ps吸管工具用不了怎么办?

我们的办公神器ps软件,大家一定是耳熟能详的吧。Adobe photoshop是电影、视频和多媒体领域的专业人士,使用3D和动画的图形和Web设计人员,以及工程和科学领域的专业人士的理想选择。Photoshop支持宽屏显示器的新式版面、集20多个窗口于一身的d…

小数据 vs 大数据:为AI另辟蹊径的可操作数据

在人工智能背景下,您可能已听说过“大数据”这一流行语,那“小数据”这一词呢,您有听说过吗?无论您听过与否,小数据都无处不在:线上购物体验、航空公司推荐、天气预报等均依托小数据。小数据即一种采用可访…

webpack 和 ts 简单配置及使用

如何使用webpack 与 ts结合使用 新建项目 ,执行项目初始化 npm init -y会生成 {"name": "tsdemo01","version": "1.0.0","description": "","main": "index.js","scripts&…

java_免费文本翻译API_小牛翻译

目录 前言 开始集成API 纯文本翻译接口 双语对照翻译接口 指定术语翻译接口 总结 前言 网络上对百度,有道等的文本翻译API集成的文章比较多,所以集成的第一篇选择了小牛翻译的文本翻译API。 小牛翻译文本翻译API,支持388个语种&#xff0…

CrossOver2023快速在Mac和Linux系统上运行Windows软件

让您可以在 Mac 和 Linux 系统上运行 Windows 应用,不必购买 Windows 授权,不必重启系统,不必使用虚拟机。通过 CrossOver, 您可以从 dock 直接启动 Windows 应用,与您的 Mac 和 Linux 系统功能无缝集成。 无需重启 Cr…

强训第35天

选择 A 经过一个1->2 经过两个2->4 开始慢增长 4->5 5->6....9->10 到达4KB时变成慢增长,最多增长到10 D 网络号是180.80.76 但题目让向主机所在子网发广播 180 .80 .(01001101 | 111111 11)79 .255 标红的两位属于主机号所以答案为D A C 分片的组…

排名算法简介:对搜索结果进行排序的主要排名算法

一、介绍 学习排名 (LTR) 是一类监督式机器学习算法,旨在根据项目与查询的相关性对项目列表进行排序。在分类和回归等问题中的经典机器学习中,目标是根据特征向量预测单个值。LTR 算法对一组特征向量进行操作,并预测项…

【FAQ】视频集中存储EasyCVR安防监控平台接入AI分析时的通道显示问题排查

安防视频监控平台视频集中存储EasyCVR可拓展性强、视频能力灵活、部署轻快,可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等,以及支持厂家私有协议与SDK接入,包括海康Ehome、海大宇等设备的SDK等。 安防监控视频云存储平台EasyCVR既具…

为什么贴片电阻的阻值不规律?为了在某精度下做到阻值的全覆盖(对,但不全对),E系列电阻的算法(E12系列值不对的猜想)

1、先放上E系列的电阻表格 E12精度10%,E24精度5%,E96精度1%; 2、以精度作为条件的演算 通常我们选择电阻,要确定电阻的精度,如果以精度作为条件。 以10%精度来说:(数值少,好算&am…

STM32 CubeMX (第二步Freertos任务通信:队列、信号量、互斥量,事件组,任务通知)

STM32 CubeMX STM32 CubeMX ____Freertos任务通信:队列、信号量、互斥量,事件组,任务通知 STM32 CubeMX一、STM32 CubeMX设置时钟配置HAL时基选择TIM1(不要选择滴答定时器;滴答定时器留给OS系统做时基)使用…

超分辨率地震速度模型

文献分享 1. Multitask Learning for Super-Resolution 原题目:Multitask Learning for Super-Resolution of Seismic Velocity Model 全波形反演(FWI)是估算地下速度模型的强大工具。与传统反演策略相比,FWI充分利用了地震波的…

计算机丢失msvcp110.dll是什么意思?如何修复?

在日常使用电脑的时候,有时候会遇到一些使用问题。 比如,有一次遇到了这样一个问题。 那就是,因为“msvcp110.dll”这个文件丢失,有些软件安装不了。 计算机丢失msvcp110.dll是什么意思?该如何修复呢? ​…

05-微信小程序常用组件-表单组件

05-微信小程序常用组件-表单组件 文章目录 表单组件button 按钮案例代码 form 表单案例代码 image 图片支持长按识别的码案例代码 微信小程序包含了六大组件: 视图容器、 基础内容、 导航、 表单、 互动和 导航。这些组件可以通过WXML和WXSS进行布局和样式设…

LC-二叉树最大深度

LC-二叉树最大深度 链接:https://leetcode.cn/problems/maximum-depth-of-binary-tree/description/ 描述: 给定一个二叉树 root ,返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 例1: …

一力破万法,Fiddler跟Charles抓包图文详解教程

一、Fiddler 1.1 Fiddler抓取HTTPS设置 1.1.1 配置证书 Tools菜单 —> Options —> HTTPS —> 勾选Decrypt HTTPS traffic选项。 说明: 勾选Decrypt HTTPS traffic选项:Decrypt HTTPS traffic:意思是解密HTTPS流量(…

[VS/C++]如何更好的配置DLL项目中的成品输出

注意,解决方案与项目不放在同一个文件夹中,即不选中图中选项 直入主题 首先右键项目选择属性,或者选中项目然后AltEnter 选择配置属性下的常规 分别在四种配置中编辑输出目录如下 注意,四种配置要分别配置,一个个来…

leetcode做题笔记86分隔链表

给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。 你应当 保留 两个分区中每个节点的初始相对位置。 示例 1: 输入:head [1,4,3,2,5,2], x 3 输出&am…