Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
RT-Thread一般讨论
一种相对简洁的RTGUI添加控件方法
发布于 2012-12-18 23:56:19 浏览:13883
订阅该版
实现如下效果, 1) 绘制4个按键,分别为上、下、左、右,并排列成特殊的形状 2)在按键up上实现点击按键后,按键字符发生变化的效果,这需要通过自己实现按键的事件处理函数实现。 3)为这四个按键都设置onbutton函数,即点击按键后修改某处label的效果 4)为了精简代码,设计了一个简单的数据结构来循环创建/添加/绘制/设置 控件,可以看成是一种简洁的解析器引擎。 使用simulator测试,效果如下: [attachment=-3] [attachment=-2] [attachment=-1] [attach]0[/attach] 源码如下。 ``` #include
#include
#include
#include
#include
#include
#include
struct rtgui_label *label; rt_bool_t btn_up_event_handle(struct rtgui_object *obj, struct rtgui_event *event) { static int status; switch (event->type) { case RTGUI_EVENT_MOUSE_BUTTON: if (((struct rtgui_event_mouse *) event)->button & RTGUI_MOUSE_BUTTON_UP) { status = !status; RTGUI_BUTTON(obj)->parent.text = (status == 0) ? "A" : "B"; } break; } return rtgui_button_event_handler(obj, event); } void btn_up_handle(struct rtgui_object *obj, struct rtgui_event *event) { rtgui_label_set_text(label, "button up pressed!"); } void btn_down_handle(struct rtgui_object *obj, struct rtgui_event *event) { rtgui_label_set_text(label, "button down pressed!"); } void btn_left_handle(struct rtgui_object *obj, struct rtgui_event *event) { rtgui_label_set_text(label, "button left pressed!"); } void btn_rgiht_handle(struct rtgui_object *obj, struct rtgui_event *event) { rtgui_label_set_text(label, "button right pressed!"); } #define SCREEN_WIDTH 320//800 #define SCREEN_HIGHT 480 #define BTN_SIZE 60 #define BTN_PAD_BASE_X ((SCREEN_WIDTH -(BTN_SIZE)*3) / 2 + BTN_SIZE) #define BTN_PAD_BASE_Y ((SCREEN_HIGHT -(BTN_SIZE)*3) / 2) #define BTN 1 #define LABEL 2 struct widget_draw { int type; char *name; struct rtgui_rect rect; void *event_handle; //void * event_handle (struct rtgui_object * obj,struct rtgui_event * event); void *para; }; static const struct widget_draw todrawtable[] = { {BTN, "Up", {BTN_PAD_BASE_X, BTN_PAD_BASE_Y, BTN_PAD_BASE_X + BTN_SIZE, BTN_PAD_BASE_Y + BTN_SIZE}, btn_up_event_handle, btn_up_handle}, {BTN, "Down", {BTN_PAD_BASE_X, BTN_PAD_BASE_Y + BTN_SIZE * 2, BTN_PAD_BASE_X + BTN_SIZE, BTN_PAD_BASE_Y + BTN_SIZE * 3}, RT_NULL, btn_down_handle}, {BTN, "Left", {BTN_PAD_BASE_X - BTN_SIZE, BTN_PAD_BASE_Y + BTN_SIZE, BTN_PAD_BASE_X, BTN_PAD_BASE_Y + BTN_SIZE * 2}, RT_NULL, btn_left_handle}, {BTN, "Right", {BTN_PAD_BASE_X + BTN_SIZE, BTN_PAD_BASE_Y + BTN_SIZE, BTN_PAD_BASE_X + BTN_SIZE * 2, BTN_PAD_BASE_Y + BTN_SIZE * 2}, RT_NULL, btn_rgiht_handle}, {LABEL, "label", {0, 0, 200, 30}, RT_NULL, "hello, world"}, }; #define RESRC_PATH "/SD/programs/tetris/resource" static void win_thread_entry(void *parameter) { struct rtgui_app *app; struct rtgui_win *win; struct rtgui_rect rect ; struct rtgui_widget *widget; int i; app = rtgui_app_create(rt_thread_self(), "MyApp"); RT_ASSERT(app != RT_NULL); /* create a full screen window */ rtgui_get_screen_rect(&rect); rtgui_set_mainwin_rect(&rect); win = rtgui_mainwin_create(RT_NULL, "MainWin", RTGUI_WIN_STYLE_NO_BORDER | RTGUI_WIN_STYLE_NO_TITLE); if (win == RT_NULL) { rtgui_app_destroy(app); return; } for (i = 0; i < sizeof(todrawtable) / sizeof(todrawtable[0]); i++) { switch (todrawtable*.type) { case BTN: { struct rtgui_button *btn; btn = rtgui_button_create(todrawtable*.name); if (btn == RT_NULL) goto _err; if (todrawtable*.para != RT_NULL) rtgui_button_set_onbutton(btn, todrawtable*.para); widget = RTGUI_WIDGET(btn); break; } case LABEL: { widget = RTGUI_WIDGET(rtgui_label_create(todrawtable*.para)); if (widget == RT_NULL) goto _err; label = RTGUI_LABEL(widget); break; } } /* 添加控件的窗口容器 */ rtgui_container_add_child(RTGUI_CONTAINER(win), widget); /* 控件布局 */ rtgui_widget_set_rect(widget, &todrawtable*.rect); if (todrawtable*.event_handle != RT_NULL) rtgui_object_set_event_handler(RTGUI_OBJECT(widget), todrawtable*.event_handle); } /* 显示主窗口 */ rtgui_win_show(win, RT_FALSE); /* 循环 */ rtgui_app_run(app); _err: rtgui_win_destroy(win); rtgui_app_destroy(app); rt_kprintf("MyApp Quit. "); } int gui3() { rt_thread_t tid; tid = rt_thread_create("win", win_thread_entry, RT_NULL, 2048, 20, 20); if (tid != RT_NULL) { rt_thread_startup(tid); } return 0; } #include
FINSH_FUNCTION_EXPORT(gui3, gui run) ``` ![1.png](https://oss-club.rt-thread.org/uploads/3230_2fa0f1df0bd7f404ed5885189f9a68f8.png) ![2.png](https://oss-club.rt-thread.org/uploads/3230_51a2a7befdb3cc3977290c2e9b74cb08.png) ![3.png](https://oss-club.rt-thread.org/uploads/3230_b68bee4aa2ef56ed2d9e5e9ed83c56a1.png) ![5.png](https://oss-club.rt-thread.org/uploads/3230_e5ebca2414676eabcf67a497927a8e1c.png) ![未命名.jpg](https://oss-club.rt-thread.org/uploads/5213_6999359f3be2c37a3f30b068e3e3d3cc.jpg) ![ui_test.png](https://oss-club.rt-thread.org/uploads/53_bb25d412c79ff810368ad11520205218.png) ![impl_ui.png](https://oss-club.rt-thread.org/uploads/53_9e640fde908bab5112e15359aa26ea6f.png)
查看更多
53
个回答
默认排序
按发布时间排序
prife
2012-12-19
这家伙很懒,什么也没写!
如果小农编写的GUI Builder可以生成 ``` struct widget_draw { int type; char *name; struct rtgui_rect rect; void *event_handle; //void * event_handle (struct rtgui_object * obj,struct rtgui_event * event); void *para; }; static const struct widget_draw todrawtable[] = { {BTN, "Up", {BTN_PAD_BASE_X, BTN_PAD_BASE_Y, BTN_PAD_BASE_X + BTN_SIZE, BTN_PAD_BASE_Y + BTN_SIZE}, btn_up_event_handle, btn_up_handle}, {BTN, "Down", {BTN_PAD_BASE_X, BTN_PAD_BASE_Y + BTN_SIZE * 2, BTN_PAD_BASE_X + BTN_SIZE, BTN_PAD_BASE_Y + BTN_SIZE * 3}, RT_NULL, btn_down_handle}, {BTN, "Left", {BTN_PAD_BASE_X - BTN_SIZE, BTN_PAD_BASE_Y + BTN_SIZE, BTN_PAD_BASE_X, BTN_PAD_BASE_Y + BTN_SIZE * 2}, RT_NULL, btn_left_handle}, {BTN, "Right", {BTN_PAD_BASE_X + BTN_SIZE, BTN_PAD_BASE_Y + BTN_SIZE, BTN_PAD_BASE_X + BTN_SIZE * 2, BTN_PAD_BASE_Y + BTN_SIZE * 2}, RT_NULL, btn_rgiht_handle}, {LABEL, "label", {0, 0, 200, 30}, RT_NULL, "hello, world"}, }; ``` 那么使用一楼中for循环里的代码,就可以直接根据这个数组生成合适的UI,也就是说RTGUI builder只需要简单的生成一个数组即可。 采用这种方式也将简化GUI builder的实现。 现在很多GUI builder都是直接生成源码,代码冗长并且难以修改,坐标充斥在整个代码中,上面的实现可以将代码和数据分离,可以很方便的调整位置。。 元芳,你怎么看?
shaolin
2012-12-19
这家伙很懒,什么也没写!
如果是这种方式,那么应该是定义一个 WIN 的数组,这个数组中描述的是该窗口下所有的控件属性和事件信息,然后有一个窗口解析器模块,负责来解析这个数组,创建出所有的控件,这是一种方式。 另一种方式是 UI 设计器设计出 UI 界面,然后直接生成创建控件的代码,这部分代码不要修改。另外创建一个文件来处理逻辑。
prife
2012-12-19
这家伙很懒,什么也没写!
>如果是这种方式,那么应该是定义一个 WIN 的数组,这个数组中描述的是该窗口下所有的控件属性和事件信息,然后有一个窗口解析器模块,负责来解析这个数组,创建出所有的控件,这是一种方式。 >另一种方式是 UI 设计器设计出 UI 界面,然后直接生成创建控件的代码,这部分代码不要修改。另外创建一个文件来处理逻辑。 --- 上面的代码只是一个demo,要想适合更多的控件,上面的结构体可以适当扩充,并且最后一个para域可以用来填充指针,指针指向其他结构体,那些结构体可以针对不同的控件专门设计,包含此控件的特有属性。这样可以最小化结构体占用空间。 现在很多UI设计器都是根据设计出的UI界面,直接生成源码,这种方式带来诸多问题 1)GUI builder实现复杂,需要生成大量的C代码,并且这种实现target上也会占用大量的代码段空间 2)GUI builder兼容性堪忧,如果RTGUI api发生变化,必然要大量修改GUI Builder源码。 3)代码和数据混合在一起,难以移植和修改,并且这样不是一个优良的设计。 4)。。。 比如说设计一个音乐播放器程序,只要使用GUI builder重新生成数组即可实现音乐播放器的移植,但是如果GUI builder生成的是大量代码,这样将会带来代码难以移植,就算移植修改量也会巨大。
xiao苦
2012-12-19
这家伙很懒,什么也没写!
想法是很好的。利用引擎来解析自动添加控件是不错,不过要考虑到不同控件是需要不同参数的。这个就算用指针挂包,也依旧会很复杂。 GUI Builder移植也是麻烦,这个工作量可是很大的
bernard
2012-12-19
这家伙很懒,什么也没写!
还没开始用box layou吧, 1.1.0版本发布后,找些GUI实际东西做做,这样应该会成长迅速。
prife
2012-12-19
这家伙很懒,什么也没写!
>还没开始用box layou吧, > >1.1.0版本发布后,找些GUI实际东西做做,这样应该会成长迅速。 --- 用过wxpython的自动布局引擎,但是RTGUI里我试图将label “hello,world”,水平和垂直居中都难以实现,目前的box 自动布局功能连这个最简单的功能都实现不了啊。
prife
2012-12-19
这家伙很懒,什么也没写!
>想法是很好的。利用引擎来解析自动添加控件是不错,不过要考虑到不同控件是需要不同参数的。这个就算用指针挂包,也依旧会很复杂。 > >GUI Builder移植也是麻烦,这个工作量可是很大的 --- 相对于现有的GUI实现方法,这个方法要简单的多。并且只是数组复杂而已。当前视图中的所有控件信息集中放在一起,修改起来也不是难事。至少比XML文件方式要简单的多。
grissiom
2012-12-19
这家伙很懒,什么也没写!
最好不要 cross-posting,要不会扰乱讨论的线索…… 把我在豆瓣上的评论粘过来了: 写的不错~ 但是实际上这个还是一个手写的思路,并不是一个 builder 的思路。 我设想的设计是 builder 生成的代码可以是冗长和繁复的,但是并不需要人去修改它,甚至不需要人去看…… 所以里面再差也无所谓的。而且其实设计器做好界面之后,界面就固定了。生成的代码直接把固定的界面给用户不好?为什么还要去“解析”呢?解析这个东西只对动态的内容才是有意义的…… 所以我觉得 builder 应该给出的是 c 文件,可以直接编译的。用户逻辑调用里面提供的一个接口,界面就有了,然后就只剩下安装 event_handler 的活了。至于那个生成的 c 文件里面多么烂都无所谓,用户不用去看……
prife
2012-12-19
这家伙很懒,什么也没写!
回复Grissiom: GUI代码占用的空间是一个可能的问题。 你这个思路也是当前主流GUI builder的思路,都是直接生成代码,且不说对GUI builder的实现更高。关于这种设计的缺点重点看四楼。 最重要的一个优点就是,这样设计,GUI builder的工作量相对来说会很少。另外,生成GUI builder生成的代码不修改是不可能的。肯定会有修改。 PS:之所以贴了一份在豆瓣,还是论坛阅读代码太费劲,代码框那么小,相比来说douban就好得多,方便阅读。
amsl
2012-12-19
这家伙很懒,什么也没写!
用一条语句创建控件,一条语句指定event_handler,一个控件两条语句搞定,这样才是最简单的. GUI builder最擅长计算位置.我也建议生成的是C文件,并且event_handler也预留好,用户只需填写内容. 控件创建API需要封装,包含位置、大小、属性等信息。方便用户微调。
撰写答案
登录
注册新账号
关注者
0
被浏览
13.9k
关于作者
prife
这家伙很懒,什么也没写!
提问
20
回答
550
被采纳
0
关注TA
发私信
相关问题
1
有关动态模块加载的一篇论文
2
最近的调程序总结
3
晕掉了,这么久都不见layer2的踪影啊
4
继续K9ii的历程
5
[GUI相关] FreeType 2
6
[GUI相关]嵌入式系统中文输入法的设计
7
20081101 RT-Thread开发者聚会总结
8
嵌入式系统基础
9
linux2.4.19在at91rm9200 上的寄存器设置
10
[转]基于嵌入式Linux的通用触摸屏校准程序
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
最新文章
1
rt-thread 小内存算法源码分析
2
env中添加lvgl软件包后,keil编译包--c99错误
3
【NXP-MCXA153】 定时器驱动移植
4
GD32F450 看门狗驱动适配
5
【NXP-MCXA153】看门狗驱动移植
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
UART
WIZnet_W5500
ota在线升级
PWM
freemodbus
flash
cubemx
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
中断
编译报错
Debug
rt_mq_消息队列_msg_queue
SFUD
keil_MDK
msh
ulog
C++_cpp
MicroPython
本月问答贡献
踩姑娘的小蘑菇
7
个答案
2
次被采纳
a1012112796
18
个答案
1
次被采纳
红枫
5
个答案
1
次被采纳
Ryan_CW
5
个答案
1
次被采纳
张世争
4
个答案
1
次被采纳
本月文章贡献
YZRD
3
篇文章
6
次点赞
catcatbing
3
篇文章
6
次点赞
lizimu
2
篇文章
11
次点赞
qq1078249029
2
篇文章
2
次点赞
xnosky
2
篇文章
1
次点赞
回到
顶部
发布
问题
分享
好友
手机
浏览
扫码手机浏览
投诉
建议
回到
底部