在Gui.c中,主要难理解的点是标签和文本程序那一块,因为用到了链表,以前写程序的时候不常用,所以不怎么熟悉。 这次就来讲讲GUI这两个控件,标签和文本。标签和文本都用到了单向链表,只不过使用的方式不一样,标签用的链表使用的是内插方式,文本用的链表使用的是末尾追加式。内插方式,开始的指针指向下一个节点,是最后插入的节点,而末尾追加式,开始的指针指向下一个节点是最开始的那一个节点。 标签和文本,在初始化页面的时候,开始创建的链表。 - void InitMenu(void)
- {
- InitLogoForm();
- InitCheckForm();
- InitWorkForm();
- InitSetupForm();
- InitServiceForm();
- InitPortForm();
- }
复制代码
每个界面用到的标签和文本控件的数量都不一样,因此使用链表就可以很方便的根据数量进行创建,而如果用其他方式,例如用数组的话,那么就得先固定一个数组的大小,那么这个大小就很难确定,但是不管怎么确定,由于不同的界面的控件数量各不相同,那么肯定会有浪费,如果说每个界面分配一个数组,这样在创建界面的时候又显得很麻烦,所以,使用链表来创建,就避开了这个问题,自动的去根据页面控件多少来创建。 下面以Work界面来讲一下创建链表的过程。 首先,工作界面如下 这里T表示文本控件,L表示标签控件,这里只是为了方便表示控件的类型和位置,在这里可以看出有2个文本控件和5个标签控件。 我们要创建这些控件进行使用的话,那么就要先相关的结构体去定义这些控件。
- static TextBox FrequencyTextBox;
复制代码
如果是创建的控件是文本,则使用结构体TextBox,如果创建的控件是标签,则是结构体Label,它们结构体里面的内容大都差不多,但是还是有区别,文本控件的数据是可以用按键进行操作修改,标签控件的数据只是负责显示而已,因此,可以发现文本控件的结构体TextBox多了表示数据最大值,数据最小值,数据步进值,数据最大步进值这四个变量,同时在文本和标签的结构体里各自可以发现。 文本:struct TextBoxSelf* NextTextBoxPointer; 标签:struct LabelSelf *NextLabelPointer; 它们在结构体里面又用自己的结构体定义自己,这就创建了控件链表的指针,这种实现的形式,我的感觉就像函数递归一样。 先来说说Menu中work界面文本的创建 其中可以看到有下面其中的两条语句,这些语句就是创建文本这个控件以及用链表来来连接起来的。 T1的创建语句: - System.Gui.Form.AddTextBox(&App.Menu.WorkForm, &FrequencyTextBox);
复制代码T2的创建语句: - System.Gui.Form.AddTextBox(&App.Menu.WorkForm, &PowerPercentTextBox);
复制代码
System.Gui.Form.AddTextBox()这个函数的本体就在Gui.c下的AddTextBox()。 去看这个函数,可以知道,每创建一个控件,首先都会把该控件结构体的内容给初始化。 - textBoxPointer->DataPointer = null;
- textBoxPointer->DataMax = 0;
- textBoxPointer->DataMin = 0;
- textBoxPointer->DataStep = 0;
- textBoxPointer->DataBigStep = 0;
-
- textBoxPointer->Digits= 0;
- textBoxPointer->Offset = 0;
- textBoxPointer->Coefficient = 1;
-
- textBoxPointer->StringBlockPointer = null;
- textBoxPointer->Align = GuiDataAlignRight;
- textBoxPointer->NextTextBoxPointer = null;
复制代码
第一个文本控件的插入是T1,T1是在以下代码执行的。 - if (formPointer->TextBoxPointer == null)
- {
- i++;
- formPointer->TextBoxPointer = textBoxPointer;
- return;
- }
复制代码
这里就会首先去判断WorkForm这个页面的文本指针指向的地址是否为空地址,为空的说明该控件是插入的第一个,因此当前页面即Work的TextBoxPointer就指向了&FrequencyTextBox的地址,然后函数就会因为return就退出了。这里要注意的是这段代码只是在第一个文本控件创建的时候使用,后面就会因为不符合判断条件而继续执行后面的代码。 然后接下来就到第二个文本控件T2,主要的话就是看这段代码 - pointer = formPointer->TextBoxPointer;
- while (pointer->NextTextBoxPointer != null){
- pointer = pointer->NextTextBoxPointer;
- }
-
- pointer->NextTextBoxPointer = textBoxPointer;
复制代码这里首先利用相同的结构体定义pointer,用来保存头结点的信息,然后利用pointer进行链表的遍历,因为T2你要插在T1的后面,因此你得找到当前的节点指向下一个地址是空的地方,然后插进去,那么这里由于只到了创建到了第2个控件T2,因此这个while判断就不符合了,因为T1指向下一个地址是空的,因此L2直接到了pointer->NextTextBoxPointer = textBoxPointer;这一句,其实就是把T1的下一个指针指向了T2,T2的下一个指针即指向了NULL,这就完成了Work界面文本控件创建的全部过程了。 注意这里的箭头是Next指针指向T2这个节点的,而不是整体指向整体,是里面的一个指针指向下一个整体。 然后来说说这个界面的标签创建的过程。 L1的创建: - System.Gui.Form.AddLabel(&App.Menu.WorkForm, &PowerLabel);
复制代码L2的创建: - System.Gui.Form.AddLabel(&App.Menu.WorkForm, &VoltageLabel);
复制代码L3的创建: - System.Gui.Form.AddLabel(&App.Menu.WorkForm, &CurrentLabel);
复制代码L4的创建: - System.Gui.Form.AddLabel(&App.Menu.WorkForm, &TemperatureLabel);
复制代码L5的创建: - System.Gui.Form.AddLabel(&App.Menu.WorkForm, &OnOffLabel);
复制代码一样的,创建标签的过程也是要初始化该结构体里包含的元素,然后就是链表的才开始创建。 该控件链表的相关代码: - labelPointer->NextLabelPointer = formPointer->LabelPointer;
- 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创建过程省略,因为大致一样,这个过程如下图: 到这里我们可以看到,文本和标签的插入方式是不一样的,这是因为文本需要按键修改,那么按键修改就需要从上到下,从左到右来修改,而标签只是显示而已,因此顺序不那么重要,使用最简单的单向链表即可。不过,如果文本控件想要使用内插式的话,有个歪方法,把创建文本控件的顺序颠倒一下应该也可以(我没试过,我只是这么想而已)。 接下来,再讲一下文本是如何切换,如何闪烁的。 首先来说说切换,当我们按下按键要切换文本的时候,将会进到以下的函数。 - static void SwitchTextBoxFocus(void)
- {
- if (FocusFormPointer->FocusTextBoxPointer == null)
- {
- FocusFormPointer->FocusTextBoxPointer = FocusFormPointer->TextBoxPointer;
- return;
- }
-
- FocusFormPointer->FocusTextBoxPointer = FocusFormPointer->FocusTextBoxPointer->NextTextBoxPointer;
- }
复制代码它就会开始判断当前的焦点指向的文本地址是不是空的,如果是空的就重新指向当前界面第一个文本控件,然后退出函数,然后该文本控件就开始闪烁了。而当我们再次按下按键切换文本的时候,将直接执行这条语句: - FocusFormPointer->FocusTextBoxPointer = FocusFormPointer->FocusTextBoxPointer->NextTextBoxPointer;
复制代码直到指向的地址是空地址才停止切换,停止闪烁,其实也不是说停止切换了,只是切换的地方是一个空地址,什么都不执行而已。 而闪烁是在解析文本控件的函数中进行的。 - static void ParseTextBox(void)
- {
- static byte Counter;
-
- TextBox * textBoxPointer;
-
- textBoxPointer = FocusFormPointer->TextBoxPointer;
-
- while(textBoxPointer != null)
- {
- if (FocusFormPointer->FocusTextBoxPointer == textBoxPointer)
- {
- if(++Counter == 8) Counter = 0;
- if (Counter < 4)
- {
- textBoxPointer = textBoxPointer->NextTextBoxPointer;
- continue;
- }
- }
-
- LabelToGuiLcd((Label *)textBoxPointer);
-
- textBoxPointer = textBoxPointer->NextTextBoxPointer;
- }
- }
复制代码 我们可以看到,这个函数首先是利用TextBox*定义一个textBoxPointer,然后textBoxPointer指向了FocusFormPointer->TextBoxPointer,就相当于指向了第一个文本控件。这时候,就可以进到while循环里面,判断文本的焦点是否跟当前的文本一致,如果一致的话,就开始进行闪烁的操作了。闪烁的大概原理是这样的,我们要显示的时候,就写数据给它,不显示的时候,就不写数据,这样交替进行,中间间隔一些时间,就达到了闪烁的效果。 而这里的代码是,在显示的时候,if(Counter < 4)判断语句就不成立,然后把数据就放写入到图形LCD屏,然后指向下一个文本,继续把数据后面的文本数据写入到图形LCD屏中,而如果是不显示的话,那么if (Counter < 4)判断语句就成立,那么就会直接把指针指向下一个文本,当前文本直接跳过,把文本数据写入到图形LCD屏中,直到最后指向下一个地址是空地址,才退出该函数。退出该函数之后,最后经过比较之后,屏幕就会更新,屏幕上就会有闪烁的效果了。
|