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

(未完待续。。。)

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++ STL容器适配器(详解)

STL容器适配器 什么是适配器,C STL容器适配器详解 在详解什么是容器适配器之前,初学者首先要理解适配器的含义。 其实,容器适配器中的“适配器”,和生活中常见的电源适配器中“适配器”的含义非常接近。我们知道,无…

后端项目打包上传服务器记录

后端项目打包上传服务器记录 文章目录 后端项目打包上传服务器记录1、项目打包2、jar包上传服务器 本文记录打包一个后端项目,上传公司服务器的过程。 1、项目打包 通过IDEA的插件进行打包: 打成一个jar包,jar包的位置在控制台可以看到。 2、…

简单介绍C++中的模板

目录 一、泛型编程 泛型编程的概念: 泛型编程举例: 二、函数模板 函数模板的概念: 函数模板的格式: 函数模板的实例化: 隐式实例化: 显式实例化: 模板参数的匹配原则: 三、类模板 类模板的格式定义: 类模…

【学会动态规划】最长湍流子数组(23)

目录 动态规划怎么学? 1. 题目解析 2. 算法原理 1. 状态表示 2. 状态转移方程 3. 初始化 4. 填表顺序 5. 返回值 3. 代码编写 写在最后: 动态规划怎么学? 学习一个算法没有捷径,更何况是学习动态规划, 跟我…

LeetCode 141.环形链表

文章目录 💡题目分析💡解题思路🔔接口源码💡深度思考❓思考1❓思考2 题目链接👉 LeetCode 141.环形链表👈 💡题目分析 给你一个链表的头节点 head ,判断链表中是否有环。 如果链表中…

怎么系统的学习机器学习、深度学习?当然是看书了

目录 前言 内容简介 学完本书,你将能够 作者简介 本书目录 京东自购链接 前言 近年来,机器学习方法凭借其理解海量数据和自主决策的能力,已在医疗保健、 机器人、生物学、物理学、大众消费和互联网服务等行业得到了广泛的应用。自从Ale…

react实现模拟弹框遮罩的自定义hook

需求描述 点击按钮用于检测鼠标是否命中按钮 代码实现 import React from react; import {useState, useEffect, useRef} from react;// 封装一个hook用来检测当前点击事件是否在某个元素之外 function useClickOutSide(ref,cb) {useEffect(()>{const handleClickOutside…

《论文阅读14》FAST-LIO

一、论文 研究领域:激光雷达惯性测距框架论文:FAST-LIO: A Fast, Robust LiDAR-inertial Odometry Package by Tightly-Coupled Iterated Kalman Filter IEEE Robotics and Automation Letters, 2021 香港大学火星实验室 论文链接论文github 二、论文概…

实践-传统深度学习

简介与安装 2 训练自己的数据集整体流程3 数据加载与预处理4 搭建网络模型5 学习率对结果的影响6 Drop-out操作7 权重初始化方法对比8 初始化标准差对结果的影响9 正则化对结果的影响10 加载模型进行测试 TensorFlow:每一步都需要自己做。 Keras:做起来更…

Electron基础篇

人生有些事,错过一时,就错过一世。 官网:简介 | Electron Electron-大多用来写桌面端软件 Electron介绍 Electront的核心组成是Chromium、Node.js以及内置的Native API,其中Chromium为Electron提供强大的UI能力,可以在不考虑兼容的情况下利…

C# 读取pcd点云文件数据

pcd文件有ascii 和二进制格式,ascii可以直接记事本打开,C#可以一行行读。但二进制格式的打开是乱码,如果尝试程序中读取,对比下看了数据也对不上。 这里可以使用pcl里的函数来读取pcd,无论二进制或ascii都可以正确读取…

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

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

php base64转图片保存本地

调用函数 public function base64(){$img $this->request->param(img);$img data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAkACQAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIy…

基于springboot的社区生活缴费系统/基于javaweb的水电缴费系统

摘 要 网络的广泛应用给生活带来了十分的便利。所以把社区生活缴费管理与现在网络相结合,利用java语言建设社区生活缴费系统,实现社区生活缴费管理的信息化。则对于进一步提高社区生活缴费管理发展,丰富社区生活缴费管理经验能起到不少的促进…

UniApp 制作高德地图插件

1、下载Uni插件项目 在Uni官网下载Uni插件项目,并参考官网插件项目创建插件项目. 开发者须知 | uni小程序SDK 如果下载下来项目运行不了可以参考下面链接进行处理 UniApp原生插件制作_wangdaoyin2010的博客-CSDN博客 2、引入高德SDK 2.1 在高德官网下载对应SD…

Spring Clould 部署 - Docker

视频地址:微服务(SpringCloudRabbitMQDockerRedis搜索分布式) 初识Docker-什么是Docker(P42,P43) 微服务虽然具备各种各样的优势,但服务的拆分通用给部署带来了很大的麻烦。 分布式系统中&…

Maven 基础之依赖管理、范围、传递、冲突

文章目录 关于依赖管理坐标和 mvnrepository 网站pom.xml 中"引"包 依赖范围依赖传递依赖冲突 关于依赖管理 坐标和 mvnrepository 网站 在 maven 中通过『坐标』概念来确定一个唯一确定的 jar 包。坐标的组成部分有&#xff1a; 元素说明<groupId>定义当前…

python3 0学习笔记之基本知识

0基础学习笔记之基础知识 &#x1f4da; 基础内容1. 条件语句 if - elif - else2. 错误铺捉try - except(一种保险策略&#xff09;3. 四种开发模式4. 函数&#xff1a;def用来定义函数的5. 最大值最小值函数&#xff0c;max &#xff0c;min6. is 严格的相等&#xff0c;is no…

微服务系列文章之 Springboot+Vue实现登录注册

一、springBoot 创建springBoot项目 分为三个包&#xff0c;分别为controller&#xff0c;service&#xff0c; dao以及resource目录下的xml文件。 UserController.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 …

matlab使用教程(17)—广度优先和深度优先搜索

1.可视化广度优先搜索和深度优先搜索 此示例说明如何定义这样的函数&#xff1a;该函数通过突出显示图的节点和边来显示 bfsearch 和 dfsearch 的可视化结果。 创建并绘制一个有向图。 s [1 2 3 3 3 3 4 5 6 7 8 9 9 9 10]; t [7 6 1 5 6 8 2 4 4 3 7 1 6 8 2]; G dig…