【第3版emWin教程】第47章 emWin6.x控件基础知识
教程不断更新中:http://www.armbbs.cn/forum.php?mod=viewthread&tid=98429
第47章 emWin6.x控件基础知识
本章节为大家讲解控件的基础知识,控件的本质就是窗口,或者说是具有特定外观效果的窗口。控件是emWin比较重要的功能,后面章节主要就是讲解各种控件的使用方法,讲解之前通过本章为大家普及一些控件的基础知识。
注意,这部分知识整理并修改自emWin手册的控件部分。
47.1 初学者重要提示
47.2 当前支持的控件
47.3 控件的重绘机制
47.4 如何使用控件
47.5 控件配置选项
47.6 通用控件API
47.7 控件中常用的公共函数
47.8 总结
47.1 初学者重要提示
1、 控件作为emWin的重点内容,建议初学者学习各种控件之前,本章节的基础知识是务必要看的。
2、 下图是中文版手册里面不同控件类型公共的API函数位置:
下图是英文版手册里面API函数的位置:
47.2 当前支持的控件
当前emWin支持的控件类型已经在前面第3章的3.3.6小节进行了讲解,这里不再赘述。
47.3 控件的重绘机制
从源码实现的角度来看,由于控件的本质就是窗口,所以窗口的重绘同样适用于控件。尽管如此,初学者往往一下子看不出这点,主要原因就在于我们使用函数WM_CreateWindow创建窗口或者使用函数FRAMEWIN_Create创建框架窗口的参数里面都有一个回调函数需要用户去实现,而控件的创建函数是没有这个参数的,因为官方已经集成好了一个默认的回调函数。如果用户想自定义回调函数也是没有问题的,调用函数WM_SetCallback配置新的回调函数即可。
因此,同窗口的重绘一样,控件的重绘也是通过函数WM_Exec()、GUI_Exec()或GUI_Delay()实现。
如果控件的某个属性被更改后,该控件的窗口(或部分窗口)会被标记为无效,但不会立即重绘,通过调用函数WM_Exec()、GUI_Exec()或GUI_Delay()才会使窗口管理器给控件回调函数发送WM_PAINT消息来执行重绘,当然,也可以调用函数WM_Paint()强制执行重绘。
47.4 如何使用控件
以后章节的重点就是讲解各种控件的使用方法,这里以进度条控件Progbar的使用方法为例,让大家有一个感性的认识:
PROGBAR_Handle hProgBar; /* 进度条句柄 */
GUI_DispStringAt("Progress bar", 100, 20);
hProgBar = PROGBAR_Create(100, 40, 100, 20, WM_CF_SHOW); /* 创建进度条 */
调用函数PROGBAR_Create就实现了进度条的创建,显示效果如下:
另外特别注意,仅创建进度条控件是显示不出来的,需要用户调用函数WM_Exec()、GUI_Exec()或GUI_Delay()才可以显示出来,调用时窗口管理器会自动绘制此控件。
每种类型的控件都有若干能够修改其外观的成员函数,控件创建后,可通过调用其成员函数更改其属性。比如要使上述创建的进度条显示45%并将进度条颜色从默认设置 (深灰/浅灰)更改为绿色/红色,可使用以下代码段:
PROGBAR_SetBarColor(hProgBar, 0, GUI_GREEN);
PROGBAR_SetBarColor(hProgBar, 1, GUI_RED);
PROGBAR_SetValue(hProgBar, 45);
更改了属性后也不是立即刷新的,需要用户调用函数WM_Exec()、GUI_Exec()或GUI_Delay()才可以显示出来。这就是一个简单的操作过程,初学者有个认识即可。
47.4.1 默认配置
所有控件都还具有一个或多个配置宏,比如所使用的字体和颜色。由于我们使用的STemWin只有库,实际测试发现这些宏配置修改后都没有效果,应该是库没有将相应的宏配置开放出来,所以要修改的话只能调用相应的API函数进行修改。
47.4.2 控件如何通信
控件通常作为子窗口创建,父窗口可为任何窗口类型,甚至是另一种控件。为了确保同步,父窗口的任何子控件,无论何时,有任何事件发生,通常都应通知父窗口。有事件发生时,子窗口控件通过发送WM_NOTIFY_PARENT消息与其父窗口通信。作为消息一部分的通知代码取决于发生的事件类型,大多数控件都定义了不同事件类型的一个或多个通知代码,具体支持哪些通知代码会在后面章节讲解控件时跟大家进行说明。
47.4.3 皮肤设置
控件的外观可使用各自的成员函数进行修改,其中一些支持皮肤设置。如果对某控件使用了皮肤设置,则“皮肤”将决定该控件的外观,且一些成员函数将失效。这一点非常重要,初学者务必要注意,经常有初学者会问,为什么设置了皮肤后,部分函数没有效果了,关键原因就在这里了。
47.4.4 控件的动态内存使用
在嵌入式应用中,由于内存碎片问题,通常都不愿意使用动态内存。但可以使用许多不同方法来避免内存碎片,由于只要内存区域被应用程序中的指针引用,这些方法的使用都有局限性。因此,emWin使用不同的方法:所有对象以及运行时存储的所有数据都存储在被句柄引用的内存区域中,这使它能够在运行时重新分配已分配的内存区域,因此避免了使用指针时会发生的长期分配问题。所以所有控件都使用句柄引用。
对于初学者来说,这个问题有个了解即可。
47.4.5 确定控件的类型
项目中有时需要获取当前的控件类型,由于STemWin没有像WM_GetWidgetType()一样的函数来确定控件的类型,其类型仅可通过将特定控件的回调函数与控件API的公共回调函数进行比较来确定。在回调函数未被覆盖时此方法很有效。如下所示为如何确定控件类型的简短示例,如果回调函数被覆盖,则需相应调整方法:
WM_CALLBACK * pCb = WM_GetCallback(hWidget); if (pCb == BUTTON_Callback) { /* Widget is a button */ } else if (pCb == DROPDOWN_Callback) { /* Widget is a dropdown */ } else if (pCb == LISTBOX_Callback) { /* Widget is a listbox */ } else if (...) { ... }
47.5 控件配置选项
emWin支持以下五种宏定义来设置控件的外观
由于emWin只有库,在文件GUIConf.h文件中设置这些宏定义也许是没有效果的,如果测试发现这些宏定没有效果就只能调用相应API函数WIDGET_SetDefaultEffect或者WIDGET_SetEffect以及相应控件的字体大小函数进行设置。下面再为大家展示如下三种宏定义时控件的显示效果。
WIDGET_USE_SCHEME_LARGE显示的效果如下:
WIDGET_USE_SCHEME_MEDIUM显示的效果如下:
WIDGET_USE_SCHEME_SMALL显示的效果如下:
WIDGET_USE_FLEX_SKIN显示的效果如下:
从emWin5.28版本开始默认情况下就是这种开启皮肤色的效果。
47.6 通用控件API
由于控件本质上就是窗口,所以它们与任何窗口管理器API例程都兼容,最常用于控件的窗口管理器函数如下所列,其实不限制下面这些函数,很多其它的窗口管理器函数都可以用于控件,窗口管理器函数中的形参hWin填控件的句柄即可:
47.7 控件中常用的公共函数
下面是控件中常用的公共函数:
这里我们重点说其中的两个函数,这两个在以后的项目中用的最多,其它的几个函数在后面章节中用到的时候再做讲解。
47.7.1 <WIDGET>_CreateIndirect()函数
这个函数在前面的几章教程中多次用到,在对话框中创建控件主要就是用这个函数,任何控件都可使用相应的前缀间接创建。例如:
BUTTON_CreateIndirect() 可间接创建一个按钮控件,
CHECKBOX_CreateIndirect() 可间接创建一个复选框控件,等等。
在对话框中创建控件推荐使用这种间接的创建方式,方便管理。当然也可以使用函数<WIDGET>_Create()直接创建它。在对话框上面创建控件时会用到一个GUI_WIDGET_CREATE_INFO结构体类型,效果如下(前面章节举的例子中用到对话框的时候都有用到这个):
/* ********************************************************************************************************* * GUI_WIDGET_CREATE_INFO类型数组 ********************************************************************************************************* */ static const GUI_WIDGET_CREATE_INFO _aDialogCreate[] = { { FRAMEWIN_CreateIndirect, "armfly", 0, 0, 0, 320,240,0,0}, { TEXT_CreateIndirect, "ARM", GUI_ID_TEXT0, 30,30,300,25, 0,0}, { BUTTON_CreateIndirect, "BUTTON0", GUI_ID_BUTTON0, 30, 70,120,40, 0,0}, { BUTTON_CreateIndirect, "BUTTON1", GUI_ID_BUTTON1, 30,125,120,40, 0,0}, };
GUI_WIDGET_CREATE_INFO结构体类型定义如下,这个非常有必要了解下:
typedef struct { GUI_WIDGET_CREATE_FUNC * pfCreateIndirect; // 间接创建函数 const char * pName; // 控件名Text (不是所有控件都需要) I16 Id; // 控件ID I16 x0, y0, xSize, ySize; // 控件的坐标位置和大小 I16 Flags; // 控件用到的标志,没有就写0 I32 Para; // 控件用到的参数,没有就写0 U32 NumExtraBytes; // 函数 <WIDGET>_SetUserData & <WIDGET>_GetUserData用到的 // 额外字节。 } GUI_WIDGET_CREATE_INFO;
上面结构体成员里面的标记Flags和参数Para是可选的,不同的控件是不一样的。如果用到这两个参数了可以直接查看相应控件的API列表,找到相应函数后查看具体说明,比如按钮的间接创建函数BUTTON_CreateIndirect()说明如下:
------------------
-------------------
按钮没有用到参数Flags和Para。如果用到这两个参数了,比如编辑框,说明就是如下这种形式:
------------------
------------------
具体用到那种控件了具体分析即可。
47.7.2 WIDGET_SetDefaultEffect()函数
通过这个函数可以设置控件的三种状态,效果如下:
这里有一点要注意,如果大家设置了控件的皮肤,那么设置这三种状态是无效的。
47.8 总结
本章节就为大家讲解这么多,通过这些基础知识让大家对控件有一个感性认识,随着后面章节开始讲解各种控件会有一个全面的认识。