STemWin 無疑非常好用,系統提供的各種機制讓我們攻城尸 敲GUI代碼如虎添翼,事半功倍
可是仍然會遇到一些問題與困惑。 比如想要的功能系統沒有提供怎么辦,想用的控件沒有提供怎么辦。
STemWin 可以自定義控件嗎? 可以更改 原控件的行為嗎? 回答是肯定改變行為通過回調函數很容易辦到,
并且網上應該有大量教程代碼,這里就不介紹了。
舉個例子,假如開發一臺檢測 重量的東西,屏幕上根據重量顯示類似強度條一樣的東西,
并且 輕的時候強度條是綠色的重則是紅色, 由輕到重 從綠色到紅色漸變。 在使用 STemWin 想要實現這個功能,
應該沒有任何控件可以實現 唯一的實現方式是使用 底層繪圖函數。,但是這里有個問題,使用底層繪圖函數
則表示你拋棄了 WM管理器的各種功能,分層,重疊,渲染,無效化,回調!等等,這會使你開發其他地方很郁悶。
筆者正是希望有這樣一個控件而著手試著自定義一個控件。并將經驗傳授給大家拋磚引玉,以后大家都有自己的自定義控件
,更自由的使用STemWin。同時為了馬克下代碼 以免忘記
下面先傳兩張效果圖
144001b7n7pzzvjb8uvsc7.png (5.91 KB, 下載次數: 38)
下載附件
2016-6-17 16:18 上傳
144002j37uv32gymfyar3x.png (6.95 KB, 下載次數: 58)
下載附件
2016-6-17 16:18 上傳
沒有注釋 大家看的順序從
Create_demowidget 到 callback 就行了。
始終記得一點 WM管理器 通過 消息(callback) 來驅動 整個系統。
比如當你 的控件需要顯示的時候 WM管理器會往你的 callback 發送一條 WM_PAINT 消息,此時你負責把控件應該有的樣子顯示出來就OK了
而這正好是我們想要。,其他消息則使用 WM_DefaultProc 去處理好了。
相信沒有注釋 你也能看懂的!
下面是代碼 .h 和.c
- #include "WM.h"
- #include "DIALOG_Intern.h" /* Req. for Create indirect data structure */
- #include "WIDGET.h"
- #include
- #include "..GUIIncludeGRAPH_Private.h"
- #define DEMO_Widget_ID 0x34455854 /* DEMO_Widget */
- #define DEMO_Widget_INIT_ID(p) p->DebugId = DEMO_Widget_ID
- typedef struct {
- WIDGET Widget;//第一項必須窗口 控件WM管理器使用
- GUI_COLOR aBkColor[2];//根據需要添加 本控件 的兩項顏色值 分別為上圖的 背景黑色 和白色
- GUI_COLOR RECT_Color;//上圖外框的藍色
- WM_HMEM hpText;//未用到
- WM_HMEM ahDrawObj[1];//未用到
- int size_base;//中間最短一條的長度
- GRAPH_PROPS Props;//盜用其他控件的 顏色, 繪外框的時候使用
- uint16_t Sta_MAX;//控件的最大條數
- uint16_t State;//當前顯示的 信息
- #if GUI_DEBUG_LEVEL >= GUI_DEBUG_LEVEL_CHECK_ALL
- int DebugId;
- #endif
- } DEMO_Widget_Obj;
- GUI_HMEM Create_demowidget(int x0, int y0, int xsize, int ysize, WM_HWIN hParent, int WinFlags, int ExFlags, int Id);
- void Set_demowidget_Sta(GUI_HMEM hObj,int state);
- void Set_demowidget_Sta_MAX(GUI_HMEM hObj,int MAX);
- void Set_demowidget_Base(GUI_HMEM hObj,int base);
復制代碼
下面是 .c
- #include "demowidget.h"
- //這個函數負責顯示 控件的外觀 主要是調用一下 GUI 2D 繪圖函數或者 有必要的話顯示文本
- void _Paint(DEMO_Widget_Obj *pObj,GUI_HMEM hObj,GUI_RECT * pRectInvalid)
- {
- GUI_RECT RectClient;
- int i,size_x;
- GUI_RECT rClient, rInside,Rect={0,0,0,0},Rect1={0,0,0,0},Rect2={0,0,0,0};
- GUI_GetClientRect(&RectClient);
- rClient = RectClient;
- Rect=rClient;
- GUI_Clear();
- GUI_SetBkColor(pObj->Props.aColor[1]);
- GUI_SetColor(pObj->Props.aColor[2]);
- GUI_DrawRectEx(&Rect);
- GUI__ReduceRect(&Rect, &Rect, 1);
- GUI_DrawRectEx(&Rect);
- Rect=rClient;
- GUI__ReduceRect(&rClient, &rClient, 3);
- LCD_SetBkColor (pObj->aBkColor[0]);
- LCD_SetColor (pObj->RECT_Color);
- size_x=(rClient.x1-pObj->size_base+1)/2/pObj->Sta_MAX;
- Rect.x0=rClient.x0+pObj->Sta_MAX*size_x;
- Rect.x1=rClient.x1-pObj->Sta_MAX*size_x;
- Rect.y1=(rClient.y1/2)+rClient.y1/(pObj->Sta_MAX*2+1)/2-1;
- Rect.y0=(rClient.y1/2)-rClient.y1/(pObj->Sta_MAX*2+1)/2;
- GUI__ReduceRect(&Rect1,&Rect,0);
- GUI__ReduceRect(&Rect2,&Rect,0);
- GUI_DrawRectEx(&Rect);
- for(i=0;iSta_MAX;i++)
- {
- Rect1.x0+=-size_x;
- Rect1.x1+=+size_x;
- Rect1.y0+=-rClient.y1/(pObj->Sta_MAX)/2;
- Rect1.y1+=-rClient.y1/(pObj->Sta_MAX)/2;
- Rect2.x0+=-size_x;
- Rect2.x1+=+size_x;
- Rect2.y0+=+rClient.y1/(pObj->Sta_MAX)/2;
- Rect2.y1+=+rClient.y1/(pObj->Sta_MAX)/2;
- GUI_DrawRectEx(&Rect1);
- GUI_DrawRectEx(&Rect2);
- }
- LCD_SetColor(GUI_GREEN);
- LCD_SetBkColor (pObj->aBkColor[0]);
- GUI__ReduceRect(&Rect1,&Rect,1);
- GUI__ReduceRect(&Rect2,&Rect,1);
- GUI_FillRectEx(&Rect1);
- for(i=0;iState;i++)
- {
- LCD_SetColor((255-((255/pObj->Sta_MAX)*i)<<8)|((255/pObj->Sta_MAX*i)));
- Rect1.x0+=-size_x;
- Rect1.x1+=+size_x;
- Rect1.y0+=-rClient.y1/(pObj->Sta_MAX)/2;
- Rect1.y1+=-rClient.y1/(pObj->Sta_MAX)/2;
- Rect2.x0+=-size_x;
- Rect2.x1+=+size_x;
- Rect2.y0+=+rClient.y1/(pObj->Sta_MAX)/2;
- Rect2.y1+=+rClient.y1/(pObj->Sta_MAX)/2;
- GUI_FillRectEx(&Rect1);
- GUI_FillRectEx(&Rect2);
- }
- LCD_SetColor(pObj->aBkColor[1]);
- for(;iSta_MAX;i++)
- {
- Rect1.x0+=-size_x;
- Rect1.x1+=+size_x;
- Rect1.y0+=-rClient.y1/(pObj->Sta_MAX)/2;
- Rect1.y1+=-rClient.y1/(pObj->Sta_MAX)/2;
- Rect2.x0+=-size_x;
- Rect2.x1+=+size_x;
- Rect2.y0+=+rClient.y1/(pObj->Sta_MAX)/2;
- Rect2.y1+=+rClient.y1/(pObj->Sta_MAX)/2;
- GUI_FillRectEx(&Rect1);
- GUI_FillRectEx(&Rect2);
- }
- }
- static void _Delete(DEMO_Widget_Obj* pObj) {
- GUI_ALLOC_FreePtrArray(pObj->ahDrawObj, GUI_COUNTOF(pObj->ahDrawObj));
- }
- void DEMO_Widget_callback(WM_MESSAGE*pMsg)
- {
- GUI_HMEM hObj=pMsg->hWin;
- DEMO_Widget_Obj * pObj;
- if (WIDGET_HandleActive(hObj, pMsg) == 0) {
- return;
- }
- pObj= (DEMO_Widget_Obj *)GUI_ALLOC_h2p(hObj);
- switch(pMsg->MsgId)
- {
- case WM_PAINT://響應重繪命令,顯示自己 WM管理器在需要顯示的時候自動發送。
- _Paint(pObj, hObj,(GUI_RECT*)pMsg->Data.p);
- return;
- case WM_DELETE:
- _Delete(pObj);
- break; /* No return here ... WM_DefaultProc needs to be called */
- //刪除控件 這里不能return 因為需要WM_DefaultProc 迭代刪除子控件
- }
- WM_DefaultProc(pMsg);
- }
- GUI_HMEM Create_demowidget(int x0, int y0, int xsize, int ysize, WM_HWIN hParent, int WinFlags, int ExFlags, int Id)
- {
- GUI_HMEM hObj;
- GUI_USE_PARA(ExFlags);
- /* Create the window */
- WM_LOCK();
- hObj = WM_CreateWindowAsChild(x0, y0, xsize, ysize, hParent, WinFlags, DEMO_Widget_callback,
- sizeof(DEMO_Widget_Obj) - sizeof(WM_Obj));
- //創建一個Window 小工具的靈魂, 靠它小工具才受WM管理器管理 各種WM管理器的功能才得以使用
- //這里需要注意 設置了N個字節的 userdata ,userdata 和創建的Window 實際使用的內存是連續的
- //
- if(hObj)
- {
- DEMO_Widget_Obj * pObj = (DEMO_Widget_Obj *)GUI_ALLOC_h2p(hObj);
- //將Window 句柄轉換為DEMO_Widget_Obj 類型的指針 這里的轉換關系 需要 仔細思考!
- //上面提到userdata 和創建的Window 實際使用的內存是連續的 實際上 Window 實際使用的內存就是 WIDGET
- //這也是為什么一開始 說 DEMO_Widget_Obj 的第一項必須是 WIDGET
- WIDGET__Init(&pObj->Widget, Id, WIDGET_STATE_FOCUSSABLE);
- DEMO_Widget_INIT_ID(pObj);//下面是設置一些默認屬性
- pObj->aBkColor[0]=GUI_BLACK;
- pObj->aBkColor[1]=GUI_WHITE;
- pObj->RECT_Color=GUI_BLUE;
- pObj->Sta_MAX=40;
- pObj->State=0;
- pObj->size_base=5;
- pObj->State=0;
- pObj->Props = GRAPH__DefaultProps;
- }
- else
- {
- GUI_DEBUG_ERROROUT_IF(hObj==0, "DEMO_Widget_Create failed")
- }
- WM_UNLOCK();
- return hObj;
- }
- //下面的函數都由應用程序 運行過程中調用,
- //按照格式改屬性然后 使用WM_InvalidateWindow 告訴 WM管理器 控件需要重繪就OK了
- void Set_demowidget_Sta(GUI_HMEM hObj,int state)
- {
- DEMO_Widget_Obj * pObj;
- if(hObj)
- {
- pObj= (DEMO_Widget_Obj *)GUI_ALLOC_h2p(hObj); /* Don't use use WIDGET_H2P because WIDGET_INIT_ID() has not be called at this point */
- }
- else
- {
- return;
- }
- WM_LOCK();
- if(state<=pObj->Sta_MAX)
- pObj->State=state;
- WM_InvalidateWindow(hObj);
- WM_UNLOCK();
- }
- void Set_demowidget_Sta_MAX(GUI_HMEM hObj,int MAX)
- {
- DEMO_Widget_Obj * pObj;
- if(hObj)
- {
- pObj= (DEMO_Widget_Obj *)GUI_ALLOC_h2p(hObj); /* Don't use use WIDGET_H2P because WIDGET_INIT_ID() has not be called at this point */
- }
- else
- {
- return;
- }
- WM_LOCK();
- pObj->Sta_MAX=MAX;
- WM_InvalidateWindow(hObj);
- WM_UNLOCK();
- }
- void Set_demowidget_Base(GUI_HMEM hObj,int base)
- {
- DEMO_Widget_Obj * pObj;
- if(hObj)
- {
- pObj= (DEMO_Widget_Obj *)GUI_ALLOC_h2p(hObj); /* Don't use use WIDGET_H2P because WIDGET_INIT_ID() has not be called at this point */
- }
- else
- {
- return;
- }
- WM_LOCK();
- pObj->size_base=base;
- WM_InvalidateWindow(hObj);
- WM_UNLOCK();
- }
復制代碼
STemWin 樹形文件 目錄 執行 初始化速度 和 內存優化
不知道大家有沒有 這樣的應用,需要將磁盤上面的文件以列表方式顯示出來。,
TREEVIEW 無疑是最佳選擇了,可是 假如存在這樣一種情況,你的設備經常需要保存文件, 比如每個小時保存一個 文件 每天或者每個月一個文件夾
也許沒這么頻繁,但是無論如何 設備使用到一定時間 磁盤里面的文件數量將不可估量。
那么問題來了
第一、 樹形目錄 一個節點消耗100字節 一個樹葉 消耗 50字節 當你的文件成千上萬的時候 你的內存消耗得起么?
第二、眾所周知磁盤操作速度很慢的,就算你獲取文件名 同樣需要操作磁盤 當你的文件成千上萬的時候 你初始化這個 樹形結構 可能都需要10多秒甚至更多。
別認為沒那么慢,筆者曾經在 NAND FLASH上使用JFFS2文件系統 從文件一個有500個子文件的文件夾中 遍歷檢索第 100-103 4個文件花的時間長達2s
當然這個 JFFS2這個文件系統有關系, 后來使用FATFS文件系統 就快了起來 但是第一點依然是很嚴重的問題
為解決這個問題 筆者提出一個方案。
初始化 TREE的時候只將第一層 節點添加進去,對于文件夾, 在節點內添加一個占位 葉節點,這樣看起來這個節點前面就有一個 +號可以被打開。
當打開這個節點的時候查看子節點是否為預添加的占位符, 如果是 再去遍歷 對應的文件夾,同時刪除占位符,添加節點。
如此 遍歷的時間由單個文件夾的文件數量決定,不會因為總數量 龐大而慢。
內存則由 打開過的文件決定, 甚至 當系統內存不足的是否可以 刪除被關閉的 節點內容 再次添加占位符。釋放內存。
那么現在方案有了 具體怎么實現呢?
這里筆者提供一個已經實現的 回調函數 callback
- TREEVIEW_ITEM_Handle Item_get_next(TREEVIEW_ITEM_Handle hItem)//用到輔助函數
- {
- TREEVIEW_ITEM_OBJ *itemobj;
- if(hItem)
- {
- itemobj=(TREEVIEW_ITEM_OBJ*)GUI_ALLOC_h2p(hItem);
- return itemobj->hNext;
- }
- return 0;
- }
- void _cbtree(WM_MESSAGE * pMsg)
- {
- int Id, NCode;
- TREEVIEW_ITEM_Handle hItem,fristitem;
- char buf[20];
- int num[2]={1,2000};
- TREEVIEW_ITEM_INFO info;
- switch (pMsg->MsgId)
- {
- case WM_KEY://攔截 按鍵消息
- {
- int pressed;
- pressed=((WM_KEY_INFO*)(pMsg->Data.p))->PressedCnt;
- NCode = ((WM_KEY_INFO*)(pMsg->Data.p))->Key;
- switch(NCode)
- {
- default:
- if(pressed)
- {
- if (NCode==GUI_KEY_RIGHT)// 樹默認right 鍵打開節點,再打開前 添加節點
- {
- hItem=TREEVIEW_GetSel(pMsg->hWin);
- TREEVIEW_ITEM_GetInfo(hItem,&info);
- if(info.IsNode)
- {
- fristitem=TREEVIEW_GetItem(pMsg->hWin,hItem,TREEVIEW_GET_FIRST_CHILD);
- TREEVIEW_ITEM_GetText(fristitem,(uint8_t*)buf,20);
- if(strcmp("space",buf)==0)//比較是否為占位符
- {
- TREEVIEW_ITEM_Delete(fristitem);
- tree_add_filelist(num,0,pMsg->hWin,hItem);//這個函數將 遍歷磁盤文件添加到樹 指定節點
- }
- }
- }
- }
- else
- {
- ((WM_KEY_INFO*)(pMsg->Data.p))->PressedCnt=1;
- TREEVIEW_Callback(pMsg);
- }
- break;
- }
- }
- break;
- case WM_TOUCH:// 這個是處理觸摸事件 比較麻煩一點 主要是獲得觸摸的地方, 遍歷TREE 取得被點擊的節點 判斷是否為 節點 然后的操作就和 按鍵一樣了
- {
- TREEVIEW_ITEM_Handle hNode;
- GUI_PID_STATE* pState;
- TREEVIEW_OBJ *treeobj;
- pState = (GUI_PID_STATE*)pMsg->Data.p;
- treeobj = (TREEVIEW_OBJ*)GUI_ALLOC_h2p(pMsg->hWin);
- if(pState->y/treeobj->Props.MinItemHeight[tr]NumVisItems+1)
- {
- hNode=TREEVIEW_GetItem(pMsg->hWin,0,TREEVIEW_GET_FIRST);
- if(hNode)
- {
- int num=pState->y/treeobj->Props.MinItemHeight +((pState->y%treeobj->Props.MinItemHeight)>0 ?1:0)-1;//計算被點擊的是第幾個節點
- if(treeobj->ScrollStateV.v>0)
- num+=treeobj->ScrollStateV.v;
- while(num--)//這個while 遍歷得到被點擊的 節點handle
- {
- TREEVIEW_ITEM_GetInfo(hNode,&info);
- if(info.IsNode)
- {
- if(info.IsExpanded)
- {
- hNode=TREEVIEW_GetItem(pMsg->hWin,hNode,TREEVIEW_GET_FIRST_CHILD);
- }
- else
- {
- hNode=TREEVIEW_GetItem(pMsg->hWin,hNode,TREEVIEW_GET_NEXT_SIBLING);
- }
- }
- else
- {
- hNode=Item_get_next(hNode);
- }
- }
- }
- TREEVIEW_ITEM_GetInfo(hNode,&info);
- if(info.IsExpanded==0)
- {
- if(info.IsNode&&(pState->x<((info.Level+1)*treeobj->Props.Indent-treeobj->ScrollStateH.v))&&
- pState->x>((info.Level)*treeobj->Props.Indent-treeobj->ScrollStateH.v)
- )
- {
- fristitem=TREEVIEW_GetItem(pMsg->hWin,hNode,TREEVIEW_GET_FIRST_CHILD);
- TREEVIEW_ITEM_GetText(fristitem,(uint8_t*)buf,20);
- if(strcmp("space",buf)==0)
- {
- TREEVIEW_ITEM_Delete(fristitem);
- tree_add_filelist(num,0,pMsg->hWin,hNode);
- }
- }
- }
- else
- {
- }
- TREEVIEW_Callback(pMsg);
- }
- }
- break;
- default:
- TREEVIEW_Callback(pMsg);
- break;
- }
- }
復制代碼
效果圖片
175310k9hu9qycbqbbcq10.jpg (28.47 KB, 下載次數: 39)
下載附件
2016-6-17 16:18 上傳
|