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

使用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 在微控制器开发板上运行切换页面的程序

(未完待续。。。)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Vue2中根据权限添加动态路由

Vue2中根据权限添加动态路由 大概记录一下主要代码 1.根据后端返回的路由列表生成左侧菜单(后端返回的数据结构中用id和pid来区别包含关系) 大概结构如下: 2.前端需要处理成包含children的树形结构 //动态生成菜单 export const gener…

超实用的40道JAVA经典算法题(含答案)

作为一名Java程序员,想要拿到一份满意的offer,就必须做好充足的准备。众所周知,算法可以说是大厂面试Java程序员的必问题。好的算法可以让性能得到万倍提升,做到毫秒级处理千万数据的程度。因此,算法的重要性不言而喻&…

eqtl-GWAS和GWAS-GWAS

目前教程中有eqtl-GWAS和GWAS-GWAS两种模式,其他模式比较少见,还未进行开发 数据类型cc为分类变量即case/control,quant为连续变量,eqtl数据默认quant coloc.abf有两个比较需要注意的点,就是数据集中N是代表样本量&am…

Docker部署ES服务,canal全量同步的时候内存爆炸,ES/Canal Adapter自动关闭,CPU100%

文章目录 问题解决方案1. 对ES的限制2. 对Canal-Adapter的限制 问题 使用canal-adapter全量同步(参考Canal Adapter1.1.5版本API操作服务,手动同步数据(4))的时候 小批量数据可以正常运行(几千条&#xf…

如何利用 EMC 模型解决能源服务提供商的瓶颈

01. 什么是合同能源管理? 合同能源管理(EMC-Energy Management Contract) 是一种新型的市场化节能机制,其实质就是以减少的能源费用来支付节能项目全部成本的节能投资方式。:节能服务公司与用能单位以契约形式约定节能项目的节能目标,节能服…

真香!主数据管理系统,企业大哥必备神器

什么是主数据? 当一家连锁商超企业的市场营销部门想要策划一场线上线下营销活动,从而为消费者提供便捷的购物体验,就需要掌握商超会员的数据。 要想拥有这些数据就需掌握顾客的交易行为,掌握其购物过程行为,甚至情感…

计算机竞赛 LSTM的预测算法 - 股票预测 天气预测 房价预测

0 简介 今天学长向大家介绍LSTM基础 基于LSTM的预测算法 - 股票预测 天气预测 房价预测 这是一个较为新颖的竞赛课题方向,学长非常推荐! 🧿 更多资料, 项目分享: https://gitee.com/dancheng-senior/postgraduate 1 基于 Ke…

低代码系列——初步认识低代码

低代码系列目录 一、初步认识低代码 二、低代码是什么 三、低代码平台的概念和分类 01.无代码开发平台 02.低代码应用平台(LCAP) 03.多重体验开发平台(MXDP) 04.智能业务流程管理套件(iBPMS) 四、低代码的能力指标 五、低代码平台jnpf 表单 报表 流程 权限 一、初步认识低代码 …

远程仓库上创建一个新的分支 `b` 并将远程分支 `a` 的内容克隆到 `b` 分支上

一、需求: 要在远程仓库上创建一个新的分支 b 并将远程分支 a 的内容克隆到 b 分支上,你可以按照以下步骤进行操作: 二、解决方案: 1. 首先,使用 git clone 命令克隆远程仓库到本地。例如,要克隆一个名为…

基于百度文心大模型创作的实践与谈论

文心概念 百度文心大模型源于产业、服务于产业,是产业级知识增强大模型。百度通过大模型与国产深度学习框架融合发展,打造了自主创新的AI底座,大幅降低了AI开发和应用的门槛,满足真实场景中的应用需求,真正发挥大模型…

角色入门02----动画蓝图

使用UE4的小白人动画,首先将它动画资产重定向。先ue4转ue5小银人,在把转换后的动画ue5转ue4给这个低模人物就动画就不会很鬼畜。 进入动画创建混合空间1D,这相当于可以组合很多动画 在跑步的混合空间里设置横坐标为Speed,最大值为400&#xf…

文末有福利 | 小海小源表情包第一弹正式上线

手机铃声提醒你有新的消息 抓紧打个招呼“来了” 收到暖心的称赞 真是按捺不住激动的小心脏啊 只要你愿意拿起书 知识的大门将为你敞开 呲溜~ 这是不是像极了努力工作一天后下班的你? 。。。。。。 看了这么多“海源”表情包 是不是觉得小海、小源愈发可爱了呢…

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

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

Java【Spring】Bean 的作用域和生命周期

文章目录 前言前言一、关于 Bean 的作用域问题引入二、Bean 的作用域1, 什么是 Bean 的作用域2, Bean 的六种作用域3, 设置 Bean 的作用域(解决开篇的问题) 三、Bean 的生命周期总结 前言 前言 各位读者好, 我是小陈, 这是我的个人主页, 希望我的专栏能够帮助到你: &#x1f4…

Linux权限系列--给普通用户添加某个命令的sudo权限

原文网址:Linux权限系列--给普通用户添加某个命令的sudo权限_IT利刃出鞘的博客-CSDN博客 简介 说明 本文介绍Linux系统如何给普通用户添加某个命令的sudo权限。 使用场景 普通开发者可能需要sudo的命令: apt-get(经常要安装软件&#x…

ROS局部路径规划器插件teb_local_planner流程梳理(上)

在我之前的文章《ROS导航包Navigation中的 Movebase节点路径规划相关流程梳理》中已经介绍过Move_base节点调用局部路径规划器插件的接口函数是computeVelocityCommands,接下来,我们就从这个函数入手梳理一下teb_local_planner功能包的工作流程。 ☆注&a…

【探索Linux】—— 强大的命令行工具 P.5(yum工具、git 命令行提交代码)

阅读导航 前言一、软件包管理器 yum1.yum的概念yum的基本指令使用例子 二、git 命令行提交代码总结温馨提示 前言 前面我们讲了C语言的基础知识,也了解了一些数据结构,并且讲了有关C的一些知识,也学习了一些Linux的基本操作,也了…

WebGL游戏站优化实录【myshmup.com】

myshmup.com 允许在浏览器中创建 shmup(射击)游戏。 你可以使用具有创意通用许可证的资源或上传自己的艺术作品和声音。 创建的游戏可以在网站上发布。 该平台不需要编码,游戏对象的配置是在用户界面的帮助下执行的。 后端是使用Django框架开…

Vue3.X 创建简单项目

一、环境安装与检查 首先,我们要确保我们安装了构建vue框架的环境,不会安装的请自行百度,有很多安装教程。检查环境 node -v # 如果没有安装nodejs请安装,安装教程自行百度 vue -V# 没有安装,请执行npm install -g v…

【探索SpringCloud】服务发现-Nacos使用

前言 在聊服务注册中心时,便提到了Nacos。这次便来认识一下。当然,这自然没有官方介绍那般详尽,权当是学习了解Nacos原理的一个过程吧。 Nacos简介 Nacos,全名:dynamic Naming And Configuration Service. 而这个名…