Gorgon_Meducer 发表于 2018-3-26 19:18:21

gamep 发表于 2018-3-26 18:18
UBOUND(c_tTopMenuItems),                            //!< menu item count

是这个吗?

不是这个。如果你不知道定义,我回头补上去。

#define UBOUND(__ARRAY)       (sizeof(__ARRAY)/(sizeof(__ARRAY)))

gamep 发表于 2018-3-27 08:08:05

Gorgon_Meducer 发表于 2018-3-26 19:18
不是这个。如果你不知道定义,我回头补上去。

知道这种用法,没搜索到这个定义,还以为故意留的,我想简单了。

wendajie123 发表于 2018-3-28 09:33:24

Gorgon_Meducer 发表于 2018-3-26 19:18
不是这个。如果你不知道定义,我回头补上去。

老师你好!!!!你这个思想好像和这个思想一致 。不知道我分析的对不对发不了链接 等级不够搜索LCD菜单程序(多层菜单)

gamep 发表于 2018-3-28 17:28:02

const menu_t c_tTopMenu = {
    (menu_item_t *)c_tTopMenuItems,                                    //!< menu item list
    UBOUND(c_tTopMenuItems),                            //!< menu item count
    NULL,                                             //!< top menu has no parent
    top_menu_engine,                                    
};

最后一个逗号是不是不需要

Gorgon_Meducer 发表于 2018-3-28 20:42:33

gamep 发表于 2018-3-28 17:28
const menu_t c_tTopMenu = {
    (menu_item_t *)c_tTopMenuItems,                                    / ...

可以不要,但留下不错。
很细心,但也不是这个问题。

Gorgon_Meducer 发表于 2018-3-28 20:43:08

wendajie123 发表于 2018-3-28 09:33
老师你好!!!!你这个思想好像和这个思想一致 。不知道我分析的对不对发不了链接 等级不够搜索LC ...

很开心我们的想法一致。

caixiong 发表于 2018-3-28 21:43:49

{:smile:}{:smile:}{:smile:}{:smile:}{:smile:}

gamep 发表于 2018-3-29 10:35:07



hmsfeng 发表于 2018-3-29 13:31:33

膜拜大神,又学习了

gamep 发表于 2018-3-29 14:15:46

#define __declare_menu_item_template(__NAME)                                    \
    typedef struct __##__NAME __NAME;
#define declare_menu_item_template(__NAME)                                    \
      __declare_menu_item_template(__NAME)


请问大师,这种预定义为什么要用两个预定义实现??

#define declare_menu_item_template(__NAME)                                    \
    typedef struct __##__NAME __NAME;

直接这样写一个不好吗??

Gorgon_Meducer 发表于 2018-3-29 21:11:10

本帖最后由 Gorgon_Meducer 于 2018-3-29 21:15 编辑

gamep 发表于 2018-3-29 10:35


前两个个都木有说对,最后一个回答正确,鼓掌。然鹅,还有一个!
更新楼主位。

Gorgon_Meducer 发表于 2018-3-29 21:13:40

gamep 发表于 2018-3-29 14:15
#define __declare_menu_item_template(__NAME)                                    \
    typedef struct ...

直接写不好。如果你填写的内容里还包涵别的宏,直接写就坑了……

JACK847070222 发表于 2018-3-29 23:01:34

佩服,学习了

wendajie123 发表于 2018-3-30 10:24:55

Gorgon_Meducer 发表于 2018-3-28 20:43
很开心我们的想法一致。

{:lol:} 老师你看了吗?那个架构

gamep 发表于 2018-3-30 10:33:19

switch(this.tState) {

里面没有

default:


不知道算不算?

coslight_dt 发表于 2018-3-30 10:39:57


强帖收藏学习

Gorgon_Meducer 发表于 2018-3-30 21:33:10

wendajie123 发表于 2018-3-30 10:24
老师你看了吗?那个架构

你觉得呢?

Gorgon_Meducer 发表于 2018-3-30 21:33:32

gamep 发表于 2018-3-30 10:33
switch(this.tState) {

里面没有


呃……不算吧

ilikemcu 发表于 2018-3-30 22:07:15

楼主说的这种管理技术的模式,让我想起当前国内排名第一的台湾大润发超市的人员储备制度,他们领导层管理最牛逼也最让下属害怕的,就是谁走了马上有人顶上,有多人也是有足够人员顶,除非集体罢工,^_^楼主也道出了国内很多人忽略的一个严重问题:很多公司的设计永远在夯地基,前面的人造了一层楼,后面的推倒再造一遍,一直在低层次上重复劳动,GDP是很多,但是高度上进展小。给楼主点赞!

Gorgon_Meducer 发表于 2018-3-31 11:26:48

ilikemcu 发表于 2018-3-30 22:07
楼主说的这种管理技术的模式,让我想起当前国内排名第一的台湾大润发超市的人员储备制度,他们领导层管理最 ...

兄弟是明眼人,其中的哀叹和绝望想必你也能体会吧。

ilikemcu 发表于 2018-3-31 15:37:25

Gorgon_Meducer 发表于 2018-3-31 11:26
兄弟是明眼人,其中的哀叹和绝望想必你也能体会吧。

因为曾经在日企呆过几年,开始的时候,日本人的一些做法让人觉得匪夷所思,感觉非常呆板,死脑筋,死教条,一点都不灵活。

后来,一个偶然的机会,听了或者说看了一部台湾余世维的《成功经理人》的讲座,里边就是讲到日本公司训练新人的做法,终于豁然开朗,虽然表面看起来日本人的效率很低,但是从长远看,他们做的每一件事,基本上都会有一个积累,经验、技术、管理,等等各方面,都会有积累,而且这些是一个可以传承继承的积累,前面的人在一个事情上看似非常低效率工作了很长时间的结果,后面的人可以毫无怀疑直接拿来用,并且会一直用下去,直到有更好的替代。这样的优点是前面的低效投入,在未来的很长的时间里一直在产生效益,在这个效益消失之前所产生的总效益,和前面看似傻乎乎的低效率投入做一个比较,其投入产出比远远高于自以为聪明的国人的那种所谓灵活机动!

日本人就是这表面的低效率,实际的高效率,太特么可怕了。以现在国人的思维,想干过日本人,简直是看不到希望。

Gorgon_Meducer 发表于 2018-3-31 17:07:59

ilikemcu 发表于 2018-3-31 15:37
因为曾经在日企呆过几年,开始的时候,日本人的一些做法让人觉得匪夷所思,感觉非常呆板,死脑筋,死教条 ...

是这样的。但故事有后续,关于日本企业的这种做法,其实长久以来一直有一种怀疑的声音,
也就是,前期的低效率是否有办法改进?答案是肯定的,现在欧美企业很多用的就是改进版
本,使得日本企业这种做法的改进版得到充分应用,且前期的效率问题也得到了充分的改善。

pchf005 发表于 2018-4-1 07:34:41

牛B杠杠的

wendajie123 发表于 2018-4-2 10:56:23

Gorgon_Meducer 发表于 2018-3-30 21:33
你觉得呢?

。。。应该看了吧!!!!!!只是想确定下思路框架是不是一样{:loveliness:}

apeng2012 发表于 2018-4-2 11:30:39

字数补丁

Gorgon_Meducer 发表于 2018-4-2 17:10:40

apeng2012 发表于 2018-4-2 11:30
字数补丁

呃,并不是这个问题,我这个格式是正确的。

chenguanghua 发表于 2018-4-4 14:45:13

本帖最后由 chenguanghua 于 2018-4-4 15:37 编辑

struct __default_menu_item_t   {
    //! inherit from base class menu_item_t
    menu_item_t;
    //! depends on your application, you can add/remove/change following members
    char                *pchTitle;                      //!< Menu Title
    char                *pchDescription;                //!< Description for this menu item
    char                chShortCutKey;                  //!< Shortcut Key value in current menu
};

①此处继承基类直接这样写: menu_item_t;
没这么用过,请教学习一下

②另外一处不明白的地方:
结构体定义含有chShortCutKey,初始化为何不用这项
default_menu_item_t c_tTopMenuItems[] = {
    {
      top_menu_item_a_handler,
      NULL,                                           //!< child menu
      "Top Menu A",
      "This is Top Menu A",
    }                                          
};

Gorgon_Meducer 发表于 2018-4-4 17:16:02

本帖最后由 Gorgon_Meducer 于 2018-4-4 17:17 编辑

chenguanghua 发表于 2018-4-4 14:45
①此处继承基类直接这样写: menu_item_t;
没这么用过,请教学习一下



关于匿名结构体的使用,我在这个帖子里有详细讨论

结构体的初始化应该是基础知识了,省略的部分会被自动初始化为0——因为例子中我确实没有用到这个内容,所以就用默认的0了。

chenguanghua 发表于 2018-4-4 18:56:19

Gorgon_Meducer 发表于 2018-4-4 17:16
关于匿名结构体的使用,我在这个帖子里有详细讨论

结构体的初始化应该是基础知识了,省略的部分会被自动 ...


惭愧,好几年没写程序了,对您的菜单架构比较感兴趣,还请不吝赐教
另外一个问题:.ptCurrentMenu这是什么用法,特别是这前面的这个点(.)
static menu_engine_cb_t s_tMenuDemo = {
    .ptCurrentMenu = &menu(TopMenu),
};

擦鞋匠 发表于 2018-4-4 19:04:49

Gorgon_Meducer 发表于 2018-3-15 06:26
故意埋了一个不影响编译和功能的笔误在里面,看看谁真正的读了代码。

大神,多了一个#endif算不算?

闲鱼翻身 发表于 2018-4-4 19:59:04

收藏学习,感谢大师

Gorgon_Meducer 发表于 2018-4-4 21:27:04

擦鞋匠 发表于 2018-4-4 19:04
大神,多了一个#endif算不算?

哪里多了?求指出,如果有,估计是笔误,我会更正。

Gorgon_Meducer 发表于 2018-4-4 21:29:58

chenguanghua 发表于 2018-4-4 18:56
惭愧,好几年没写程序了,对您的菜单架构比较感兴趣,还请不吝赐教
另外一个问题:.ptCurrentMenu这是 ...

这是C99(如果我没有记错的话)引入的一种初始化结构体的方法……这种方法甚至可以用来初始化数组——也就是你可以指定只初始化哪个数组元素。

结构体初始化了解一下。

chenguanghua 发表于 2018-4-5 07:07:30

struct __default_menu_item_t   {
    //! inherit from base class menu_item_t
    menu_item_t;
    //! depends on your application, you can add/remove/change following members
    char                *pchTitle;                      //!< Menu Title
    char                *pchDescription;                //!< Description for this menu item
    char                chShortCutKey;                  //!< Shortcut Key value in current menu
};

struct __menu {
    menu_item_t      *ptItems;                        //!< menu item list
    uint_fast8_t      chCount;                        //!< menu item count
    menu_t             *ptParent;                     //!< parent menu;
    menu_engine_t      *fnEngine;                     //!< engine for process current menu
};
__menu作为容器, ptItems的定义采用default_menu_item_t是不是比较好?
当然menu_item_t也没问题,因为 pchTitle 这些不是必须的。

Gorgon_Meducer 发表于 2018-4-5 17:12:00

chenguanghua 发表于 2018-4-5 07:07
__menu作为容器, ptItems的定义采用default_menu_item_t是不是比较好?
当然menu_item_t也没问题,因为 p ...

如果使用default_menu_item_t在menu_t中,那么menu_t就失去了基类的扩展性——因为绑定死了某一个派生类,
如果别人实现了自己的xxxxx_menu_item_t类型,是不是就没法正确使用menu_t了?(因为假设按照你的建议,
menu_t里面使用了default_menu_item_t而不是 menu_item_t)

chenguanghua 发表于 2018-4-5 23:44:44

Gorgon_Meducer 发表于 2018-4-5 17:12
如果使用default_menu_item_t在menu_t中,那么menu_t就失去了基类的扩展性——因为绑定死了某一个派生类 ...


请教default_menu_item_t和menu_t的问题,其实是看到这块代码,结构体指针的强制类型转换
如果要使用pchTitle,pchDescription这些成员,结构体指针的强制类型转换会不会出现数据访问的不安全性?
static menu_engine_cb_t s_tMenuDemo = {
    .ptCurrentMenu = &menu(TopMenu),
};

Gorgon_Meducer 发表于 2018-4-6 03:39:00

chenguanghua 发表于 2018-4-5 23:44
请教default_menu_item_t和menu_t的问题,其实是看到这块代码,结构体指针的强制类型转换
如果要使用pch ...

具体举个例子?

chenguanghua 发表于 2018-4-6 07:14:54

本帖最后由 chenguanghua 于 2018-4-6 10:20 编辑

Gorgon_Meducer 发表于 2018-4-6 03:39
具体举个例子?
转:
比如在一个项目中,有大量的数据结构,他们都是双向链表,但又想共用一套对链表的操作算法,这怎么做到呢,C中又没有C++中的继承,不然我可以继承一父(类中只有两个指针,一个向前一个向后),而其算法可以写在你类中的虚函数中,供子类使用。如:

class Links
{
public:
    Links* back;
    Links* forword;

    virtual Add(){ ... };
    virtual Del(){ ... };
    virtual Ins(){ ... };
    virtual Print() =0;
    ....
};

于是对于特定的数据结构我们可以:
class mylinks : public Links
{
public:
    char* myname;
    char sex;
    intage;
    ...
    virtual Print(){ .... }
};

对其操作时都可以使用你类的泛型算法。
在C中,该如何做呢?我们用C中的指针和强制类型转可以做到。

下面是我总结出来的一个小的程序,体现了用指针的弹性来实现这一继承的效果:
(我在Liniux下的GCC调试通过)

#include <stdio.h>

/* 双向链表 (类似于父类)*/
typedef struct hLinks{
   struct hLinks *bwLink;
   struct hLinks *fwLink;
} hLinks;

/*
* 一个使用双向链表的结构
*   (类似于子类)
*/
typedef struct hEnt{
   hLinks links;
   int hData;
   char key;
} hEnt;

/*
*双向链插入泛型算法 (类似于父数中的成员函数)
*/
Insert( hLinks* elem, hLinks *dest )
{
   if ( !elem || !dest ) return;

   elem->bwLink = dest->bwLink;
   elem->fwLink = dest;
   dest->bwLink->fwLink = elem;
   dest->bwLink = elem;
}

/*
*打印 (类似于子类重载父类的成员函数)
*/
PrintLink( hLinks *h )
{
    hEnt *p ;

    for( p = ( hEnt* ) h->fwLink;   /*<-----------把hLink再转回来*/
         p != ( hEnt* ) h;
         p = ( hEnt* )( (hLinks*)p )->fwLink )
    {
       printf("hData=[%d], key=[%s]/n", p->hData, p->key);
    }
}

main()
{
   hLinks head;
   hEnt a;
   int i;

   head.bwLink = &head;
   head.fwLink = &head;

   for(i=0;i<4;i++)
   {
      a.hData = i*10;
      sprintf(a.key,"id=%d", i);

      /*使用泛型算法构造双向链*/
      Insert( (hLinks *) &a, (hLinks *) &head ); /*   <-----注意这个强制转换*/
   }

   PrintLink( (hLinks *) &head ); /*   <-------也注意这个强制转换*/
}

chenguanghua 发表于 2018-4-6 07:29:43

本帖最后由 chenguanghua 于 2018-4-6 10:21 编辑


可以看出,在前两个内存单元中两个结构体存储的内容是相同的,当然不管相不相同计算机是不管的,
当hLinks类型转换成hEnt类型时,计算机就将原结构体看做是hEnt类型的。
转换后的hEnt类型结构体的前面两个内存单元的内容就是hLinks中的前两个单元内容,
而hEnt的后两个内存单元中的内容取得是hLinks后的两个单元(这两个单元不是hLinks类型的成员,而是别的内容,所以如果转换后的hEnt要访问hData和key的话是不安全的!)。

/*----------------------------------------*/
续:对宏的定义不熟悉,理解错了
/*----------------------------------------*/

如果在default_menu_engine想通过指针访问pchDescription这些成员,是否可以通过结构体指针进行强制类型转换得到?
比如:
LCD_disp(((default_menu_item_t *)(this.ptCurrentMenu->ptItems))->pchDescription);                  

xiangbin099 发表于 2018-4-6 08:16:09

学习,学习

Gorgon_Meducer 发表于 2018-4-6 17:53:12

本帖最后由 Gorgon_Meducer 于 2018-4-6 18:29 编辑

chenguanghua 发表于 2018-4-6 07:29
可以看出,在前两个内存单元中两个结构体存储的内容是相同的,当然不管相不相同计算机是不管的,
当hLinks ...

我明白你的意思了,其实这里涉及到一个本质上的问题:C语言不是 Type-Safe 的,对于强制类型转换,如果不在使用时
加以一定的规则限制,否则无论如何都会出现你担心的问题。实际上,规则很简单:

规定,强制类型转换只能从派生类向基类进行,反之则禁止——除非你对传入指针的类型非常确信(然而这一点通常是很
难做到的)。

在这个例子中,default_menu_item_t就是 menu_item_t 的派生类,因此,用menu_item_t 来访问仅属于 menu_item_t
的成员是安全的;同时绝对禁止通过 menu_item_t 类型的指针经过强制类型转化以后来访问派生类的成员。

如果你看我另外一个帖子,就会知道,其实还有另外一个安全的做法:
https://www.amobbs.com/thread-5582609-1-1.html?_dsign=4d743ffc

注意这里:



//! \brief macro for inheritance

#define INHERIT_EX(__TYPE, __NAME)  \
            union {                 \
                __TYPE  __NAME;     \
                __TYPE;             \
            };

/*! \note When deriving a new class from a base class, you should use INHERIT
*        other than IMPLEMENT, although they looks the same now.
*/
#define INHERIT(__TYPE)             INHERIT_EX(__TYPE, base__##__TYPE)

/*! \note You can only use IMPLEMENT when defining INTERFACE. For Implement
*        interface when defining CLASS, you should use DEF_CLASS_IMPLEMENT
*        instead.
*/
#define IMPLEMENT(__INTERFACE)      INHERIT_EX(__INTERFACE, base__##__INTERFACE)

/*! \note if you have used INHERIT or IMPLEMENT to define a CLASS / INTERFACE,
          you can use OBJ_CONVERT_AS to extract the reference to the inherited
          object.
  \*/
#define OBJ_CONVERT_AS(__OBJ, __INTERFACE)  (__OBJ.base__##__INTERFACE)



当我们要访问基类/接口时(这里支持多重继承),使用OBJ_CONVERT_AS(__OBJ, __INTERFACE) 宏,而不是
直接强制类型转换,这种方法就从根本上避免了由 强制类型转换带来的入侵问题——也就是说:

我们要尽可能在C语言的面向对象实践中遵守Type-Safe 的规则。

进一步说,就是借由INHERIT() 和 IMPLEMENT() 宏实现的接口/类继承,我们在访问时也要通过原有类型来进行,
而不是强制类型转换——这就是OBJ_CONVERT_AS() 存在的本意——当然,支持多个接口也是用途之一。

另外,对于你说的 链表操作,你提到了C++的泛型,其实C的宏配合一定代码模板实现出来的效果是一样的:

比如你看这个链表操作的例子:

https://github.com/GorgonMeducer/Generic_MCU_Software_Infrastructure/blob/master/sources/gmsi/utilities/template/t_list.h

比如,你看这个堆的泛型(C source code + macro),就是用了上述链表进行实现的,同时这个堆的泛型还能根据不同的目标类型进行专用堆类的生成。

https://github.com/GorgonMeducer/Generic_MCU_Software_Infrastructure/tree/master/sources/gmsi/service/memory/epool

在上述的例子中,你可以注意到,我遵守了“只允许将派生类向基类进行强制类型转换”的原则——在派生类中,指针都是指向派生类,而不是
基类的,这就避免了出现你说的问题。

关于C语言的面向对象实践,欢迎进行讨论。


更进一步说,在C语言中构建 type-safe 的面向对象实践,当年深入浅出MFC给出过详细的分析,简单说就是在每一个抽象类的结构体里,
第一个成员都会包含一个指向最基础的基类的指针,又叫 type 类,大体定义如下:


typedef const struct __type_t type_t;
struct {
    type_t *ptBase;                                           //!< 类型的继承链
    const char *chTypeName;
    void *constructor(void *, ...);                        //!< 构造函数
    void *destructor(void *);                              //!< 析构函数
};



为了让这一工作变得更自然,通常会定义一个object类:

typedef struct {
    type_t   *ptType;
} object_t


基于这一结构,所有的 abstract 类 都会 继承object 类,而所有abstract 类的派生类则无需这么做,只需要将object类的ptType指针重新初始化即可。

可以看出,type是借助 type_t 静态成员实现的一个链表,这个链表在编译时刻完成初始化。借助这个链表,我们可以实现is_type_of() 这样的操作,甚至还能借助获取
type类型实现动态的构造和析构,如果你更进一步的对 type_t 进行继承和派生,甚至可以把派生类的虚函数表一起放到派生出来的 xxxx_type_t
里面(也就是放在constructor和destructor后面)……

最后的最后,借助 is_type_of ,我们能完成最基本的 type-safe 的检查——如果你觉得很有必要的话——这只不过是一个简单的指针判断而已。

说了这么多,其实已经把C++的物理层面的实现说的差不多了……这种时候,如非必要,真心不如直接用C++。我觉得没有必要,所以只做了很基本
的事情——制定规则,遵守规则,尽可能不乱用强制类型转换。


最后针对你的具体问题:

如果在default_menu_engine想通过指针访问pchDescription这些成员,是否可以通过结构体指针进行强制类型转换得到?
比如:
LCD_disp(((default_menu_item_t *)(this.ptCurrentMenu->ptItems))->pchDescription);                  

答案是:不行,因为default_menu_engine 只知道什么是menu_item_t,并不知道default_menu_item_t的存在,因此不应该出现基类的成员函数访问
派生类的成员变量的问题。

如果你要访问default_menu_item_t,那么理论上你应该有一个针对 default_menu_item_t 的 menu_engin函数,由于menu_t 是 menu_item_t 的容器,
menu_t 里面有一个指针指向专门针对当前容器内 menu_item_t 的对应 menu_engine(这就是面向对象的意义,数据和方法打包),因此在当前的menu
下,用专用的menu_engine处理对应的 xxxx_menu_item_t 是肯定安全的。楼主位的例子中, default_menu_engine只是一个范例,它展示了基本的菜单
导航操作的处理,你可以注意到,里面连 显示函数都没有调用,所以它真的只是一个伪代码——用来做demo演示这个菜单的数据结构如何工作的。

flyfox8 发表于 2018-4-9 16:28:27

虽然不合时宜,我还是想说全文我只关注到了小姐姐一段。{:lol:}

liangyi518 发表于 2018-4-9 16:33:45

厉害兄弟!{:victory:}

l.htlht 发表于 2018-4-9 19:55:41

收藏 学习

lqzhw 发表于 2018-4-9 21:23:56

哎 水平不行 各种不懂都是裸奔加状态机标志都是随意定义只要自己看得懂

Gorgon_Meducer 发表于 2018-4-10 18:55:20

lqzhw 发表于 2018-4-9 21:23
哎 水平不行 各种不懂都是裸奔加状态机标志都是随意定义只要自己看得懂 ...

这也是裸奔加状态机啊。哪里看不懂,我给你详细说说。

xieweibiao 发表于 2018-4-10 22:22:00

大神的东西就是精辟

gamep 发表于 2018-4-11 17:58:18

请问大师,在多级菜单中,想实现,在任意菜单,长按菜单键返回最顶层菜单。
怎么实现才比较优雅呢?

QP的方案是用层次状态机做类似设计。但是,QP相关的组件相对少些。
很想听听大师对层次状态机的讲解和意见。

另外,本主题中讲到的菜单都包含菜单项。在实时数据显示界面,就是数据在更新,
好像不含菜单项。是否就不属于本主题的讨论范围?

Gorgon_Meducer 发表于 2018-4-11 23:07:48

gamep 发表于 2018-4-11 17:58
请问大师,在多级菜单中,想实现,在任意菜单,长按菜单键返回最顶层菜单。
怎么实现才比较优雅呢?



你说的问题基本在93楼有讨论。
层次状态机不就是子状态机调用么……不知道你想知道啥……

长按回到顶层,抛开长按(按键扫描或者按键消息),回到顶层……不就是顺着父指针一路向上么……
你具体遇到什么问题了呢?

gamep 发表于 2018-4-12 14:27:17

我再慢慢领悟

hpdell 发表于 2018-4-12 16:22:56

正在找牛逼的菜单程序,感谢感谢,有空试试

hy2515131 发表于 2018-4-12 16:26:31

LZ开贴讲解,厉害啊!

hy2515131 发表于 2018-4-12 16:29:55

有没有结合按键KEY与菜单menu的交换程序吗?

Gorgon_Meducer 发表于 2018-4-12 19:53:37

hy2515131 发表于 2018-4-12 16:29
有没有结合按键KEY与菜单menu的交换程序吗?

看 default_menu_engine, 在楼主位

gao_hailong 发表于 2018-4-15 08:40:41

Gorgon_Meducer 发表于 2018-4-12 19:53
看 default_menu_engine, 在楼主位

请教傻大侠:我想把结构体同时用于独立定义和结构体嵌套,并且嵌套时用匿名结构,应该怎么操作。
例如:
typedef struct {}childstr;
typedef struct {childstr cstr;}fatherstr;
此时嵌套的结构体不是匿名的,调用起来太麻烦。

hy2515131 发表于 2018-4-15 09:05:16

Gorgon_Meducer 发表于 2018-4-12 19:53
看 default_menu_engine, 在楼主位

多谢多谢!

wangyeqing333 发表于 2018-4-15 09:51:08

感觉菜单、GUI这些还是适合用面向对象的思路来写

Gorgon_Meducer 发表于 2018-4-15 21:17:38

gao_hailong 发表于 2018-4-15 08:40
请教傻大侠:我想把结构体同时用于独立定义和结构体嵌套,并且嵌套时用匿名结构,应该怎么操作。
例如:
...

答案在142楼里提到的几个宏,不明白再问。

Gorgon_Meducer 发表于 2018-4-15 21:18:30

wangyeqing333 发表于 2018-4-15 09:51
感觉菜单、GUI这些还是适合用面向对象的思路来写

没错,就是要用OO的思想

cjc2010 发表于 2018-4-21 18:41:38

赞。            

gao_hailong 发表于 2018-4-21 20:25:06

Gorgon_Meducer 发表于 2018-4-15 21:17
答案在142楼里提到的几个宏,不明白再问。

直接用声明,不加变量名就行了,谢谢大侠。

bigk2000 发表于 2018-4-21 20:48:45

多谢多谢!

淋湿的鸡毛 发表于 2018-5-16 11:32:43

常看常新

我要吃大葱 发表于 2018-5-22 20:24:32

纽币,很幸运能够学习前辈的编程思想。

ztrx 发表于 2018-5-23 07:20:46

rtos下怎么玩

dtdzlujian 发表于 2018-5-23 07:30:28

好好学习,认真听课

Gorgon_Meducer 发表于 2018-5-23 18:40:42

ztrx 发表于 2018-5-23 07:20
rtos下怎么玩

没有区别啊……你的具体担心点在哪里?可以详细描述下你的疑惑么?

ztrx 发表于 2018-5-23 23:53:13

任务循环里要加延时吗,延时多长

Gorgon_Meducer 发表于 2018-5-24 00:27:24

本帖最后由 Gorgon_Meducer 于 2018-5-24 00:33 编辑

ztrx 发表于 2018-5-23 23:53
任务循环里要加延时吗,延时多长


你说的任务循环是超级循环么?还是说在RTOS下任务里面的超级循环?
为什么要加延时?目的是什么?

如果加延时的目的是为了让出处理器时间给其它任务,我想说,这是很通用的多任务协作问题,跟菜单引擎没有任何关系。

认真说起来,在多任务环境下,应该是不需要你说的这个延迟的。因为多任务环境下,运行菜单的任务应该是受按键队列
驱动的——如果按键队列为空,则阻塞该任务,如果按键队列不为空,则读取按键并进行菜单操作——这里值得注意的是,
最好把显示和菜单任务分离开,菜单任务只是个后台骨架,而显示(尤其是你需要有动态效果的情况下)应该由一个独立的
任务来完成,这个任务接受来自菜单的消息来决定如何处理前台显示的问题。我觉得,如果说延时,恐怕是这个前台显示需
要延时——主要是处理类似gif的效果,或者是滚动字幕之类的。

从这个角度来说,负责菜单的任务是不需要任何延时的,它是完全由数据流(按键队列)驱动的。这是一个典型的生产者消
费者模型。

希望这个回答解决了你的疑问。如果还有什么问题,请提的具体一些。

ztrx 发表于 2018-5-24 07:50:29

谢谢,学习 了

bigwei 发表于 2018-7-5 20:59:55

{:victory:} 谢谢分享,收藏学习!

netking2012 发表于 2018-7-11 09:18:21

大师一出必是精品啊

CH_anyin 发表于 2018-7-11 12:49:21

感谢分享,学习了!

gentlerain 发表于 2018-7-11 15:47:40

先收藏再细看

cgbabc 发表于 2018-7-18 21:03:35

有个问题想请教一下,每个菜单内都有一个需要调整的参数,如何把按键的动作(加或者减)与菜单关联起来?有什么好的解决方案吗?

yunqing_abc 发表于 2018-7-19 16:23:06

大师强帖必须顶!!!
数据结构真的很重要

Gorgon_Meducer 发表于 2018-7-19 18:00:27

cgbabc 发表于 2018-7-18 21:03
有个问题想请教一下,每个菜单内都有一个需要调整的参数,如何把按键的动作(加或者减)与菜单关联起来?有 ...

菜单只是个导航,你看到的所谓菜单里有个值要修改那是错觉。
要修改的值实际上是通过窗体里的控件来实现的,而真正的菜单只是帮你打开这个窗体而已。
如果你一直试图把菜单和窗体混淆来看……估计就没法降低菜单的耦合度和复杂度了。

cgbabc 发表于 2018-7-19 19:23:09

Gorgon_Meducer 发表于 2018-7-19 18:00
菜单只是个导航,你看到的所谓菜单里有个值要修改那是错觉。
要修改的值实际上是通过窗体里的控件来实现 ...

好吧,我以前一都按菜单就是一个参数来看待,看来需要改变这种观念。还需要另外实现一个窗体的程序来实现调整参数。

Gorgon_Meducer 发表于 2018-7-20 18:40:28

cgbabc 发表于 2018-7-19 19:23
好吧,我以前一都按菜单就是一个参数来看待,看来需要改变这种观念。还需要另外实现一个窗体的程序来实现 ...

分开以后,你会发现更多模块化的机会。

R8C 发表于 2018-7-23 19:06:31

看不明白,如果用emwin还要这么写吗?

Gorgon_Meducer 发表于 2018-7-24 17:53:49

R8C 发表于 2018-7-23 19:06
看不明白,如果用emwin还要这么写吗?

用已有的架构就不用这么写了。这个主要是从原理上帮助一些好奇的人,现代的比较先进的嵌入式GUI从原理上
说是如何处理类似的事情的——当然很多系统用了消息机制,不过本质上与这个结构并不冲突。

R8C 发表于 2018-7-24 21:03:56

谢谢回帖,明白了

lhhsea2004 发表于 2018-7-24 22:23:32

先收藏,慢慢消化

ailibuli 发表于 2018-8-14 14:07:48

楼主菜单技术精湛,最近在研究开源遥控器,无线协议和摇杆混控都能看懂,但是菜单部分完全没有头绪,不知道楼主收能帮忙看看:https://github.com/DeviationTX/deviation/tree/master/src/gui
这个菜单还是很不错的,在开源遥控领域,Deviation也是老大哥,他的GUI貌似是为每一个Button注册回调,然后菜单怎么做的,就完全看不明白了。

ailibuli 发表于 2018-8-14 14:08:46

Gorgon_Meducer 发表于 2018-7-24 17:53
用已有的架构就不用这么写了。这个主要是从原理上帮助一些好奇的人,现代的比较先进的嵌入式GUI从原理上
...

楼主,可以一起讨论下这个吗?

楼主菜单技术精湛,最近在研究开源遥控器,无线协议和摇杆混控都能看懂,但是菜单部分完全没有头绪,不知道楼主收能帮忙看看:https://github.com/DeviationTX/deviation/tree/master/src/gui
这个菜单还是很不错的,在开源遥控领域,Deviation也是老大哥,他的GUI貌似是为每一个Button注册回调,然后菜单怎么做的,就完全看不明白了。

Gorgon_Meducer 发表于 2018-8-14 21:06:15

ailibuli 发表于 2018-8-14 14:08
楼主,可以一起讨论下这个吗?

楼主菜单技术精湛,最近在研究开源遥控器,无线协议和摇杆混控都能看懂, ...

没有认真看,实在没有时间,但我猜它所谓的菜单只是一个窗体吧……既然给每个按钮都加了回调,那更有可能就只是一个普通的窗体了。

mengtiantang 发表于 2018-8-15 17:31:03

好吧,看到和大神的差距了

asbzhang 发表于 2018-8-17 09:09:01

这个菜单结构帮助了不少人

曾经的诸葛二蛋 发表于 2018-8-23 17:31:20

后排听课,瞻仰大神的帖子

gaoxiaohu2018 发表于 2018-9-2 00:25:21

标记复制+粘贴

xiaoliusheng 发表于 2018-9-28 22:37:50

争取早点能看懂...{:lol:}

Gorgon_Meducer 发表于 2018-9-28 22:53:41

xiaoliusheng 发表于 2018-9-28 22:37
争取早点能看懂...

争取早点用起来是比较实际的。

xxc007 发表于 2018-9-28 23:03:01


强帖收藏学习

LB342342 发表于 2018-10-21 09:47:35

谢谢大神的好贴。说下自己读后的感受:
对于初学者来说,读懂并且使用确实很难,要花大量的时间,如果能以一个实例来展开(就想前面的说的出一个工程),会很快掌握。
这个帖子,对高手来说写的太详细,对初学者来说又有些晦涩难懂。
高不成,低不就。

LB342342 发表于 2018-10-21 09:51:02

真心想用,真心不好懂。{:smile:}

Gorgon_Meducer 发表于 2018-10-22 17:33:50

LB342342 发表于 2018-10-21 09:51
真心想用,真心不好懂。

其实你要相信我,这里我已经尽最大努力把内容写得让初学者好懂了。但看来效果并不尽如人意,
这里,我需要你的帮助,你能说出第一个让你觉得不好懂得地方么?我从那里开始尝试进行改进。

一步一步的,如果你不介意,我就可以借助你的帮助,把教程写得非常简单。其实你说用一个例子,
我讲真的, 我从头开始进行推导这个框架怎么来的,本身就是一个例子了……并且后面也的确提供
了例子——比如单级菜单的例子和两级菜单的例子。我想知道的是,这里缺失的是什么?

maimaige 发表于 2018-10-22 17:41:18

我先 留个名字 然后 好好 研究一下 啊

LB342342 发表于 2018-10-24 09:31:11

楼主| 发表于 前天 17:33 | 只看该作者
其实你要相信我,
****************************
是我没有静下心来理解,只想着拿来就往里套用。
现在在静静的看了。
就开头部分而言,前面的推理倒把我绕糊涂了,其实前面的就是铺垫,直接从这里开始看,到时号理解点,看不懂了,再往前面看。

因此,前面的默认模板就可以用极为简单的形式进行描述:



typedef struct __menu_itemmenu_item_t;
typedef struct __menu      menu_t;

typedef fsm_rt_t menu_item_handler_t(menu_item_t *);

struct __menu_item {
    menu_item_handler_t *fnHandle;                      //!< handler
    menu_t            *ptChild;                     //!< Child Menu
};


declare_menu_item_template(default_menu_item_t)

def_menu_item_template(default_menu_item_t)
    //! depends on your application, you can add/remove/change following members
    char                *pchTitle;                      //!< Menu Title
    char                *pchDescription;                //!< Description for this menu item
    char                chShortCutKey;                  //!< Shortcut Key value in current menu
end_def_menu_item_template(default_menu_item_t)
复制代码

再次谢谢你的耐心。

LB342342 发表于 2018-10-24 12:03:06

试着把代码放到.h和.c文件,然后编译,会发现许多没有定义的标识符,然后开始在文章里找标识符的出处。
对初学者来说,要是最后给一个最终版的.c和.h文件内容。就比较好用好懂了吧。
感觉有用的信息,要在里面找,然后整合到一起。

LB342342 发表于 2018-10-24 15:07:35

我试着在MDK的STM32一个项目里加入一个.h和一个.c文件编译:
.H:
#ifndef __MENU__
#define __MENU__
#include "stm32f1xx_hal.h"

#ifndef __FSM_RT_TYPE__
#define __FSM_RT_TYPE__
//! \name finit state machine state
//! @{
typedef enum {
    fsm_rt_err          = -1,    //!< fsm error, error code can be get from other interface
    fsm_rt_cpl          = 0,   //!< fsm complete
    fsm_rt_on_going   = 1,   //!< fsm on-going
    fsm_rt_wait_for_obj = 2,   //!< fsm wait for object
    fsm_rt_asyn         = 3,   //!< fsm asynchronose complete, you can check it later.
} fsm_rt_t;
//! @}

#endif


typedef struct __menu_itemmenu_item_t;
typedef struct __menu      menu_t;

typedef fsm_rt_t menu_item_handler_t(menu_item_t *);

struct __menu_item {
    menu_item_handler_t *fnHandle;                      //!< handler
    menu_t            *ptChild;                     //!< Child Menu
};

typedef struct __menu_engine_cb menu_engine_cb_t;
typedef fsm_rt_t menu_engine_t(menu_engine_cb_t *);

struct __menu {
    menu_item_t      *ptItems;                        //!< menu item list
    uint8_t      chCount;                        //!< menu item count
    menu_t             *ptParent;                     //!< parent menu;
    menu_engine_t      *fnEngine;                     //!< engine for process current menu
};



typedef struct __default_menu_item_tdefault_menu_item_t;

struct __default_menu_item_t   {

    //! inherit from base class menu_item_t
    menu_item_t;
//..\User\0aa_menu\MENU.h(48): error:#3093: anonymous structs are only supported in --gnu mode, or when enabled with #pragma anon_unions

    //! depends on your application, you can add/remove/change following members
    char                *pchTitle;                      //!< Menu Title
    char                *pchDescription;                //!< Description for this menu item
    char                chShortCutKey;                  //!< Shortcut Key value in current menu
};

#endif
/////////////////////////////////
.C:
#include "MENU.h"

extern fsm_rt_t top_menu_engine(menu_engine_cb_t *ptThis);

extern fsm_rt_t top_menu_item_a_handler(menu_item_t *ptItem);
extern fsm_rt_t top_menu_item_b_handler(menu_item_t *ptItem);
extern fsm_rt_t top_menu_item_c_handler(menu_item_t *ptItem);

extern const menu_t c_tTopMenu;

default_menu_item_t c_tTopMenuItems[] = {
    {
      top_menu_item_a_handler,
      NULL,                                           //!< child menu
      "Top Menu A",
      "This is Top Menu A",
    },
    {
      top_menu_item_b_handler,
      NULL,                                           //!< child menu
      "Top Menu B",
      "This is Top Menu B"
    },
    {
      top_menu_item_c_handler,
      NULL,                                           //!< child menu
      "Top Menu C",
      "This is Top Menu C"
    }
};

const menu_t c_tTopMenu = {
    (menu_item_t *)c_tTopMenuItems,                                    //!< menu item list
    UBOUND(c_tTopMenuItems),                            //!< menu item count UBOUND未定义
..\User\0aa_menu\menu.c(34): error:#59: function call is not allowed in a constant expression
    NULL,                                             //!< top menu has no parent
    top_menu_engine,                                    
};


fsm_rt_t top_menu_item_a_handler(menu_item_t *ptItem)
{
    return fsm_rt_cpl;
}

fsm_rt_t top_menu_item_b_handler(menu_item_t *ptItem)
{
    return fsm_rt_cpl;
}

fsm_rt_t top_menu_item_c_handler(menu_item_t *ptItem)
{
    return fsm_rt_cpl;
}

fsm_rt_t top_menu_engine(menu_engine_cb_t*ptThis)
{
    return fsm_rt_cpl;
}

编译后,出现3个错误,上面红色部分。
页: 1 [2] 3
查看完整版本: [交流][专题]再谈菜单技术 2018-03-14 Update