请选择 进入手机版 | 继续访问电脑版

雨滴科技技术论坛

 找回密码
 立即注册
查看: 1000|回复: 4

msOS GUI部分控件理解

[复制链接]

87

主题

440

帖子

2016

积分

金牌会员

Rank: 6Rank: 6

积分
2016
扫一扫,手机访问本帖
发表于 2017-7-30 20:33:21 | 显示全部楼层 |阅读模式
在Gui.c中,主要难理解的点是标签和文本程序那一块,因为用到了链表,以前写程序的时候不常用,所以不怎么熟悉。
这次就来讲讲GUI这两个控件,标签和文本。标签和文本都用到了单向链表,只不过使用的方式不一样,标签用的链表使用的是内插方式,文本用的链表使用的是末尾追加式。内插方式,开始的指针指向下一个节点,是最后插入的节点,而末尾追加式,开始的指针指向下一个节点是最开始的那一个节点。
标签和文本,在初始化页面的时候,开始创建的链表。
  1. void InitMenu(void)
  2. {                          
  3.     InitLogoForm();
  4.     InitCheckForm();
  5.     InitWorkForm();
  6.     InitSetupForm();
  7.     InitServiceForm();
  8.     InitPortForm();
  9. }
复制代码

         每个界面用到的标签和文本控件的数量都不一样,因此使用链表就可以很方便的根据数量进行创建,而如果用其他方式,例如用数组的话,那么就得先固定一个数组的大小,那么这个大小就很难确定,但是不管怎么确定,由于不同的界面的控件数量各不相同,那么肯定会有浪费,如果说每个界面分配一个数组,这样在创建界面的时候又显得很麻烦,所以,使用链表来创建,就避开了这个问题,自动的去根据页面控件多少来创建。
         下面以Work界面来讲一下创建链表的过程。
         首先,工作界面如下
1.png
         这里T表示文本控件,L表示标签控件,这里只是为了方便表示控件的类型和位置,在这里可以看出有2个文本控件和5个标签控件。
         我们要创建这些控件进行使用的话,那么就要先相关的结构体去定义这些控件。

  1. static TextBox FrequencyTextBox;
复制代码

如果是创建的控件是文本,则使用结构体TextBox,如果创建的控件是标签,则是结构体Label,它们结构体里面的内容大都差不多,但是还是有区别,文本控件的数据是可以用按键进行操作修改,标签控件的数据只是负责显示而已,因此,可以发现文本控件的结构体TextBox多了表示数据最大值,数据最小值,数据步进值,数据最大步进值这四个变量,同时在文本和标签的结构体里各自可以发现。
文本:struct TextBoxSelf* NextTextBoxPointer;
标签:struct LabelSelf *NextLabelPointer;
它们在结构体里面又用自己的结构体定义自己,这就创建了控件链表的指针,这种实现的形式,我的感觉就像函数递归一样。
先来说说Menu中work界面文本的创建
其中可以看到有下面其中的两条语句,这些语句就是创建文本这个控件以及用链表来来连接起来的。
T1的创建语句:
  1. System.Gui.Form.AddTextBox(&App.Menu.WorkForm, &FrequencyTextBox);
复制代码
T2的创建语句:
  1. System.Gui.Form.AddTextBox(&App.Menu.WorkForm, &PowerPercentTextBox);
复制代码

System.Gui.Form.AddTextBox()这个函数的本体就在Gui.c下的AddTextBox()。
         去看这个函数,可以知道,每创建一个控件,首先都会把该控件结构体的内容给初始化。
  1.     textBoxPointer->DataPointer = null;
  2.     textBoxPointer->DataMax = 0;
  3.     textBoxPointer->DataMin = 0;
  4.     textBoxPointer->DataStep = 0;
  5.     textBoxPointer->DataBigStep = 0;
  6.    
  7.     textBoxPointer->Digits= 0;
  8.     textBoxPointer->Offset = 0;
  9.     textBoxPointer->Coefficient = 1;
  10.    
  11.     textBoxPointer->StringBlockPointer = null;
  12.     textBoxPointer->Align = GuiDataAlignRight;
  13.     textBoxPointer->NextTextBoxPointer = null;
复制代码

第一个文本控件的插入是T1,T1是在以下代码执行的。
  1.     if (formPointer->TextBoxPointer == null)
  2.     {
  3.                    i++;
  4.                    formPointer->TextBoxPointer = textBoxPointer;
  5.                    return;
  6. }
复制代码

这里就会首先去判断WorkForm这个页面的文本指针指向的地址是否为空地址,为空的说明该控件是插入的第一个,因此当前页面即Work的TextBoxPointer就指向了&FrequencyTextBox的地址,然后函数就会因为return就退出了。这里要注意的是这段代码只是在第一个文本控件创建的时候使用,后面就会因为不符合判断条件而继续执行后面的代码。
然后接下来就到第二个文本控件T2,主要的话就是看这段代码
  1.     pointer = formPointer->TextBoxPointer;
  2.     while (pointer->NextTextBoxPointer != null){
  3.         pointer = pointer->NextTextBoxPointer;
  4.          }
  5.         
  6.     pointer->NextTextBoxPointer = textBoxPointer;
复制代码
这里首先利用相同的结构体定义pointer,用来保存头结点的信息,然后利用pointer进行链表的遍历,因为T2你要插在T1的后面,因此你得找到当前的节点指向下一个地址是空的地方,然后插进去,那么这里由于只到了创建到了第2个控件T2,因此这个while判断就不符合了,因为T1指向下一个地址是空的,因此L2直接到了pointer->NextTextBoxPointer = textBoxPointer;这一句,其实就是把T1的下一个指针指向了T2,T2的下一个指针即指向了NULL,这就完成了Work界面文本控件创建的全部过程了。
2.png
注意这里的箭头是Next指针指向T2这个节点的,而不是整体指向整体,是里面的一个指针指向下一个整体。
然后来说说这个界面的标签创建的过程。
L1的创建:
  1. System.Gui.Form.AddLabel(&App.Menu.WorkForm, &PowerLabel);
复制代码
L2的创建:
  1. System.Gui.Form.AddLabel(&App.Menu.WorkForm, &VoltageLabel);
复制代码
L3的创建:
  1. System.Gui.Form.AddLabel(&App.Menu.WorkForm, &CurrentLabel);
复制代码
L4的创建:
  1. System.Gui.Form.AddLabel(&App.Menu.WorkForm, &TemperatureLabel);
复制代码
L5的创建:
  1. System.Gui.Form.AddLabel(&App.Menu.WorkForm, &OnOffLabel);
复制代码
一样的,创建标签的过程也是要初始化该结构体里包含的元素,然后就是链表的才开始创建。
该控件链表的相关代码:
  1.     labelPointer->NextLabelPointer = formPointer->LabelPointer;
  2. formPointer->LabelPointer = labelPointer;
复制代码
这个相比文本控件的链表就简单一点,因为这个是内插式,不需要去遍历。
那么L1到L5是怎么依次创建的呢?
有如下过程:
L1:L1的next指向了work的LabelPointer指向的地址,即NULL,然后,work的LabelPointer重新指向当前控件的地址即&PowerLabel;
L2:L2的next指向了work的LabelPointer指向的地址,即&PowerLabel,然后,work的LabelPointer重新指向了当前创建控件的地址& VoltageLabel;
L3、L4、L5创建过程省略,因为大致一样,这个过程如下图:
3.png
         
         到这里我们可以看到,文本和标签的插入方式是不一样的,这是因为文本需要按键修改,那么按键修改就需要从上到下,从左到右来修改,而标签只是显示而已,因此顺序不那么重要,使用最简单的单向链表即可。不过,如果文本控件想要使用内插式的话,有个歪方法,把创建文本控件的顺序颠倒一下应该也可以(我没试过,我只是这么想而已)。
         接下来,再讲一下文本是如何切换,如何闪烁的。
首先来说说切换,当我们按下按键要切换文本的时候,将会进到以下的函数。
  1. static void SwitchTextBoxFocus(void)
  2. {
  3.     if (FocusFormPointer->FocusTextBoxPointer == null)
  4.     {
  5.         FocusFormPointer->FocusTextBoxPointer = FocusFormPointer->TextBoxPointer;
  6.         return;
  7.     }
  8.    
  9.     FocusFormPointer->FocusTextBoxPointer = FocusFormPointer->FocusTextBoxPointer->NextTextBoxPointer;
  10. }
复制代码
它就会开始判断当前的焦点指向的文本地址是不是空的,如果是空的就重新指向当前界面第一个文本控件,然后退出函数,然后该文本控件就开始闪烁了。而当我们再次按下按键切换文本的时候,将直接执行这条语句:
  1. FocusFormPointer->FocusTextBoxPointer = FocusFormPointer->FocusTextBoxPointer->NextTextBoxPointer;
复制代码
直到指向的地址是空地址才停止切换,停止闪烁,其实也不是说停止切换了,只是切换的地方是一个空地址,什么都不执行而已。
         而闪烁是在解析文本控件的函数中进行的。
  1. static void ParseTextBox(void)
  2. {
  3.     static byte Counter;
  4.    
  5.     TextBox * textBoxPointer;

  6.     textBoxPointer = FocusFormPointer->TextBoxPointer;

  7.     while(textBoxPointer != null)
  8.     {
  9.         if (FocusFormPointer->FocusTextBoxPointer == textBoxPointer)
  10.         {
  11.             if(++Counter == 8) Counter = 0;
  12.             if (Counter < 4)
  13.             {
  14.                 textBoxPointer = textBoxPointer->NextTextBoxPointer;
  15.                 continue;
  16.             }
  17.         }

  18.         LabelToGuiLcd((Label *)textBoxPointer);
  19.       
  20.         textBoxPointer = textBoxPointer->NextTextBoxPointer;
  21.     }
  22. }
复制代码
         我们可以看到,这个函数首先是利用TextBox*定义一个textBoxPointer,然后textBoxPointer指向了FocusFormPointer->TextBoxPointer,就相当于指向了第一个文本控件。这时候,就可以进到while循环里面,判断文本的焦点是否跟当前的文本一致,如果一致的话,就开始进行闪烁的操作了。闪烁的大概原理是这样的,我们要显示的时候,就写数据给它,不显示的时候,就不写数据,这样交替进行,中间间隔一些时间,就达到了闪烁的效果。
而这里的代码是,在显示的时候,if(Counter < 4)判断语句就不成立,然后把数据就放写入到图形LCD屏,然后指向下一个文本,继续把数据后面的文本数据写入到图形LCD屏中,而如果是不显示的话,那么if (Counter < 4)判断语句就成立,那么就会直接把指针指向下一个文本,当前文本直接跳过,把文本数据写入到图形LCD屏中,直到最后指向下一个地址是空地址,才退出该函数。退出该函数之后,最后经过比较之后,屏幕就会更新,屏幕上就会有闪烁的效果了。

回复

使用道具 举报

81

主题

888

帖子

2940

积分

金牌会员

Rank: 6Rank: 6

积分
2940
发表于 2017-7-30 21:37:09 | 显示全部楼层
虽然看不懂给你个赞
回复 支持 反对

使用道具 举报

362

主题

1862

帖子

9065

积分

版主

Rank: 7Rank: 7Rank: 7

积分
9065
发表于 2017-7-30 22:32:13 | 显示全部楼层
看你的描述,基本上看懂了GUI部分,Label内插而TextBox尾链的原因也理解了,不错!
回复 支持 反对

使用道具 举报

248

主题

1461

帖子

6837

积分

论坛元老

Rank: 8Rank: 8

积分
6837
发表于 2017-7-30 23:58:43 | 显示全部楼层
不错,把最难理解的给理解了
《《 雨滴科技 》》
雨润万物,滴水成河
回复 支持 反对

使用道具 举报

0

主题

3

帖子

12

积分

新手上路

Rank: 1

积分
12
发表于 2019-2-1 09:16:35 | 显示全部楼层
换个分辨率的屏,比如 192*64、240*128、或者干脆320*240……
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

在线客服
在线咨询
咨询热线
0755-26787502-8006/8016
扫一扫二维码
直接访问本站

QQ|Archiver|手机版|小黑屋|雨滴科技  

GMT+8, 2019-9-16 02:52 , Processed in 0.075456 second(s), 28 queries , Gzip On.

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表