zoukankan      html  css  js  c++  java
  • 嵌入式GUI FTK设计与实现主循环



    转载时请注明出处和作者联系方式
    文章出处:http://www.limodev.cn/blog
    作者联系方式:李先静 <xianjimli@gmail.com>

    带图形用户界面(GUI)的应用程序和传统的批处理程序是不同的:

    * 批处理程序是一步一步的执行,直到完成任务为止,完成任务后程序立即退出。
    * 图形用户界面应用程序则是事件驱动的,它等待事件发生,然后处理事件,如此循环,直到用户要求退出为止。

    两种执行模型如下图所示:

    cmp

    通常我们把等待事件/处理事件的循环称为主循环(MainLoop),主循环是GUI应用程序必要组件之一,FTK当然也离开不主循环 (MainLoop)。大多数嵌入式GUI都采用了Windows类似的主循环:

        while(GetMessage(&msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

    我不太喜欢这种主循环,主要原因有两点:

    * 它看似简洁,但内部实现并不简洁。它从当前线程的队列里获取消息,然后处理消息。这些消息从来哪里的?当然是由其它线程传递给当前线程的,这就意味着 GUI需要多线程的支持。而FTK不想引入多线程来折磨自己,至少在GUI部分是不想的。

    * 这是面向过程的处理方式。消息是一个对象,按照面向对象的思想来说,对象的数据和行为应该绑定在一起。而这里的消息是纯粹的数据,所有消息都由目标窗口的消息处理函数来处理的。FTK希望每类消息都有自己的处理函数,而不是全部由窗口来处理。

    FTK采用了GTK类似的主循环:

        ftk_run();

    它看起来更简洁,内部实现也不需要多线程的支持。这里采用了POSA(面向模式的软件架构)中的Reactor模式,它的主要好处有:

    * 用单线程处理多个事件源。
    * 增加新事件源时不会影响事件分发的框架。

    整个主循环由下列组件构成:
    mainloop
    FtkSource 是一个接口,是所有事件源的抽象,它要求事件源实现下列函数:

    * ftk_source_get_fd 用来获取文件描述符,当然这个文件描述符不一定是真正的文件描述符,只要是MainLoop能挂在上面等待的句柄(Handle)即可。
    * ftk_source_check 用来检查事件源要求等待的时间。-1表示不关心等待时间。0表示要马就有事件发生,正数表示在指定的时间内将有事件发生。
    * ftk_source_dispatch 用来处理事件,每个事件源都有自己的处理函数,而不是全部耦合到窗口的处理函数中。

    FtkSourcesManager负责管理所有事件源。主要提供下列函数:

    * ftk_sources_manager_add 增加一个事件源。
    * ftk_sources_manager_remove 删除一个事件源。
    * ftk_sources_manager_get_count 获取事件源总数。
    * ftk_sources_manager_get 获取指定索引的事件源。

    FtkMainLoop负责循环的等待事件发生,然后调用事件源的处理函数去处理。主要提供下列函数:

    * ftk_main_loop_run 启动主循环
    * ftk_main_loop_quit 退出主循环
    * ftk_main_loop_add_source 增加一个事件源
    * ftk_main_loop_remove_source 删除一个事件源

    FtkMainLoop提供了add_source和remove_source两个函数对 FtkSourcesManager相应函数进行包装,这里包装不是简单的调用FtkSourcesManager的函数,而是发送一个事件:


    为什么要多此一举呢?原因这样的:FTK是单线程的,GUI线程只负责用户界面的管理,由后台工作的线程负责长时间的操作。但是后台工作的线程经常需要更新用户界面,比如下载网络数据的线程要更新下载进度界面。FTK需要提供一种机制,让后台线程来更新用户界面但又不需要引入互斥机制。这可以通过 idle来串行化对GUI的操作,后台线程要更新GUI时,就增加一个idle source,后台线程不能直接调用ftk_sources_manager_add,那样需要互斥机制,而且也不能唤醒主循环去处理这个idle。所以它通过Primary Source的管道发送一个事件,这个事件会唤醒睡眠中的主循环,然后调用Primary Source分发函数去处理事件。

    现在我们来看ftk_main_loop_run的实现,ftk_main_loop_run的实现是平台相关的,对于支持select的平台,用 select是最好的方法。下面是基于select的实现:

    1.找出最小等待时间和文件描述符


    这里遍历所有source,找出一个最小的等待时间和要等待的文件描述符。

    2. 等待事件发生


    3.检查事件源并调用相应的事件处理函数

     

    如果事件源处理函数的返回值不是RET_OK的事件,我们认为出错了或者是事件要求自己被移除,那就把它移除掉。

    GUI是事件驱动的,创建一个窗口后,函数马上就返回了,窗口中的控件对用户事件处理是在以后的事件循环中进行的。这对于大多数情况是正常的,但有时我们需要用户确认一些问题,根据确认的结果做相应的处理。比如,用户删除一个文件,我们要确认他是否真的想删除后才能去删除。也就是在创建对话框后,函数不是马上返回,而且等用户确认,关闭对话框后才返回。

    为了做到这一点,我们要在一个事件处理函数中,创建另外一个主循环来分发事件。模态对话框就是这样实现的:



    对话模型提供了一个用于退出该主循环的函数:

  • 相关阅读:
    215. Kth Largest Element in an Array
    214. Shortest Palindrome
    213. House Robber II
    212. Word Search II
    210 Course ScheduleII
    209. Minimum Size Subarray Sum
    208. Implement Trie (Prefix Tree)
    207. Course Schedule
    206. Reverse Linked List
    sql 开发经验
  • 原文地址:https://www.cnblogs.com/zhangyunlin/p/6167392.html
Copyright © 2011-2022 走看看