AWTK開發UI簡單入門C語言篇


AWTK 開發 UI 簡單入門 – C 語言篇

一,前序

  在上一篇教程中(AWTK 的 Window 開發環境安裝教程),我們已經配置好 AWTK 的開發環境了,今天我們使用 C 語言寫一個簡單的小例子,讓大家更加容易理解 AWTK 的工作原理。
  在 windows 平台上面開發,馬上想到的開發工具就是 vs 了,作為宇宙最強的 IDE,在開發上的便捷性和易用性都是沒得說的,雖然我們也可以使用 vscode 作為開發工具,但是為了讓大家更加簡單的理解 AWTK 這一個 GUI 的用法,所有我決定采用 vs 作為開發工具,並且不會采用 scons 來生成項目,盡量簡單化一點,讓大家看的明白。(畢竟在 windows 上開發,應該大部分人都會用 vs 這個 IDE 的吧)
  本章節中,采用的代碼為 ZLG 提供的 HelloWorld-Demo 項目為原型來介紹如果做一個簡單的 GUI,其界面為下圖:
在這里插入圖片描述

備注:

  1. 雖然本文采用 ZLG 提供的 HelloWorld-Demo 項目為原型來介紹,但UI 界面只是大致一樣,同時為了更好的讓讀者了解,所以其代碼會修改過,其目的是盡可能的使用最簡單的代碼和邏輯帶讀者入門。
  2. 附上 ZLG 提供 HelloWorld-Demo 項目的 github 地址:https://github.com/zlgopen/awtk-examples
  3. 附上本人修改后的 HelloWorld-Demo 項目的 github 地址:https://github.com/WNsACE/CSDN_AWTK_DEMO

二,建立項目

  本章節采用 vs2017 作為 IDE,所以下面的截圖都是 vs2017 的界面,其他版本的 vs,其實都差不多。
  在例子中出現的 “D:\OpenLibraries\awtk\awtk” 為 AWTK 源碼的路徑,需要根據具體情況來對應修改 電腦上面的 AWTK 源碼路徑。

1.創建空項目

  使用 vs2017 創建一個新的 c++ 空項目,並修改名字為HelloWorld-Demo,如下圖:
在這里插入圖片描述

2.配置項目

  1. 把平台改為x64,如下圖:
    在這里插入圖片描述

備注:因為 AWTK 默認編譯為 64 位的類庫。

  1. 給項目新建兩個 .c 文件,分別名為 app_main.c 和 window_main.c。

備注:這兩個 .c 文件是空文件,沒有任何東西的。

  1. 給項目加入 AWTK 相關的頭文件,如下圖:
    在這里插入圖片描述

頭文件路徑為:

  1. D:\OpenLibraries\awtk\awtk\src
  2. D:\OpenLibraries\awtk\awtk\src\ext_widgets;
  1. 給項目加入對應的宏,如下圖:
    在這里插入圖片描述

這里主要是加入的宏分別是: WIN32 。

  1. 給項目加入 AWTK 相關類庫,如下圖:
    在這里插入圖片描述在這里插入圖片描述

類庫路徑:D:\OpenLibraries\awtk\awtk\lib;
類庫名字:assets.lib;awtk.lib;base.lib;glad.lib;gpinyin.lib;linebreak.lib;nanovg.lib;SDL2.lib;tkc.lib;widgets.lib;winmm.lib;imm32.lib;version.lib;
備注:這里先不解釋各個類庫的作用,留到后面再講,而這里的類庫只是加入最基礎的只是可以讓本demo跑起來的最少類庫。

  1. 其中 AWTK 的類庫為:
    assets.lib,awtk.lib,base.lib,glad.lib,gpinyin.lib,linebreak.lib,nanovg.lib,SDL2.lib,tkc.lib,widgets.lib。
  2. 系統類庫為:winmm.lib,imm32.lib,version.lib。

3.部署資源

  由於本項目中只用到很少的資源,只需要把 AWTK 的少量資源拷貝過來就可以了,本文暫時不介紹如何配置資源和生成資源。
  在這個 demo 中主要是資源分別是字體資源和風格資源,風格資源是必須要的(每一個 AWTK 的項目都必須要有一個 default 風格),而字體資源的話,如果項目中需要顯示文字的話,則需要增加字體資源,否則可以不需要,接下來把 AWTK 源碼中的資源直接拷貝過來。

  1. 把在程序目錄下創建 res 的文件夾,如下圖。
    在這里插入圖片描述

備注:為了讓代碼結構好看一點,所以講上面創建的 app_main.c 和 window_main.c 放到 src 文件夾中。

  1. 把 D:\OpenLibraries\awtk\awtk\demos 目錄下的 assets 文件夾拷貝到剛剛創建的 res 的文件夾中,如下圖:
    在這里插入圖片描述
  2. 把 res 文件夾下多余用不到的文件刪除。(這一步其實不做也是無所謂的,只不過為了讓后面大家更好理解而已)

需要刪除的文件分別是:( ./表示 HelloWorld-Demo 項目路徑)

  1. ./res/assets/dark (文件夾)
  2. ./res/assets/README.md (文件)
  3. ./res/assets/default/inc (文件夾)
  4. ./res/assets/default/raw/data (文件夾)
  5. ./res/assets/default/raw/images (文件夾)
  6. ./res/assets/default/raw/scripts (文件夾)
  7. ./res/assets/default/raw/strings (文件夾)
  8. ./res/assets/default/raw/ui (文件夾)
  9. ./res/assets/default/raw/xml (文件夾)
  10. ./res/assets/default/raw/fonts/ap.ttf (文件)
  11. ./res/assets/default/raw/fonts/default_full.ttf (文件)
  12. ./res/assets/default/raw/fonts/README.md (文件)
  13. ./res/assets/default/raw/fonts/text.txt (文件)

注意:在 ./res/assets/default/raw/styles文件夾下,除了 default.bin 和 default.xml 兩個文件以外全部刪除。

三,編寫項目

1. 打開 app_main.c 文件,並寫入下面的代碼:

 1 #include "awtk.h"
 2 
 3 extern ret_t application_init(void);
 4 
 5 int main(void)
 6 {
 7     int lcd_w = 800;
 8     int lcd_h = 480;
 9 
10     /* 
11     * 初始化 AWTK
12     * 參數 APP_DESKTOP 為設置 window 的桌面模式 
13     * 參數 "res" 為設置資源目錄路徑為程序工作目錄下"res"
14     */
15     tk_init(lcd_w, lcd_h, APP_DESKTOP, NULL, "res");
16 
17     /* 預加載名為 default.tff 的字體資源 */
18     assets_manager_preload(assets_manager(), ASSET_TYPE_FONT, "default");
19     /* 預加載名為 default.bin 的風格資源 */
20     assets_manager_preload(assets_manager(), ASSET_TYPE_STYLE, "default");
21 
22     /* 初始化資源 */
23     tk_init_assets();
24 
25     /* 打開主屏幕 */
26     application_init();
27 
28     /* 進入awtk事件循環 */
29     tk_run();
30 
31     return 0;
32 }

 

2. 打開 window_main.c 文件,並寫入下面的代碼:

 1 #include "awtk.h"
 2 #include "awtk.h"
 3 extern ret_t application_init(void);
 4 
 5 widget_t* label_4_btn = NULL;    //遞增數值label控件指針
 6 widget_t* label_4_edit = NULL;    //顯示文本框label控件指針
 7 
 8 /**
 9  * Label文本的數值 + offset
10  */
11 static ret_t label_add(widget_t* label, int32_t offset)
12 {
13     if (label)
14     {
15         int32_t val = 0;
16         if (wstr_to_int(&(label->text), &val) == RET_OK)
17         {
18             char text[32];
19             val += offset;
20             val = tk_max(-200, tk_min(val, 200));
21             tk_snprintf(text, sizeof(text), "%d", val);
22             widget_set_text_utf8(label, text);
23 
24             return RET_OK;
25         }
26     }
27 
28     return RET_FAIL;
29 }
30 
31 /**
32  * 遞增按鈕事件
33  */
34 static ret_t on_inc_click(void* ctx, event_t* e)
35 {
36     label_add(label_4_btn, 1);
37 
38     return RET_OK;
39 }
40 
41 /**
42  * 遞減按鈕事件
43  */
44 static ret_t on_dec_click(void* ctx, event_t* e)
45 {
46     label_add(label_4_btn, -1);
47 
48     return RET_OK;
49 }
50 
51 /**
52  * 正在編輯事件
53  */
54 static ret_t on_changing(void* ctx, event_t* evt)
55 {
56     widget_t* target = WIDGET(evt->target);
57     widget_set_text(label_4_edit, target->text.str);
58 
59     return RET_OK;
60 }
61 
62 /**
63  * 初始化
64  */
65 ret_t application_init(void)
66 {
67     widget_t* win = window_create(NULL, 0, 0, 0, 0);
68 
69     /* 創建文本框*/
70     label_4_edit = label_create(win, 160, 96, 480, 40);
71     widget_set_text(label_4_edit, L"hello world");
72     widget_set_name(label_4_edit, "label_4_edit");
73 
74     /* 創建編輯框 */
75     widget_t* edit = edit_create(win, 160, 196, 480, 40);
76     edit_set_input_type(edit, INPUT_TEXT);
77     widget_set_text(edit, L"hello world");
78     widget_on(edit, EVT_VALUE_CHANGING, on_changing, NULL);
79 
80     /* 創建遞減按鈕 */
81     widget_t* dec_btn = button_create(win, 160, 288, 160, 40);
82     widget_set_text(dec_btn, L"dec");
83     widget_on(dec_btn, EVT_CLICK, on_dec_click, NULL);
84 
85     /* 創建label顯示遞增數值 */
86     label_4_btn = label_create(win, 320, 288, 160, 40);
87     widget_set_text(label_4_btn, L"88");
88     widget_set_name(label_4_btn, "label_4_btn");
89 
90     /* 創建遞增按鈕 */
91     widget_t* inc_btn = button_create(win, 480, 288, 160, 40);
92     widget_set_text(inc_btn, L"inc");
93     widget_on(inc_btn, EVT_CLICK, on_inc_click, NULL);
94 
95     return RET_OK;
96 }

四,分析代碼

  其實把上面代碼拷貝到文件中,點擊編譯和運行就可以看到本文一開始的 UI 效果圖。
  但是大部分人都想知道為啥,其實每一行的代碼都是代表着什么意思呢?所以這一環節就是配合着上面的代碼注釋來解釋關鍵性代碼的作用。

1. app_main.c 文件的 tk_init 函數

 1 /**
 2  * @method tk_init
 3  * 初始化TK。
 4  * @alias init
 5  * @annotation ["static", "scriptable"]
 6  * @param {wh_t} w LCD寬度。
 7  * @param {wh_t} h LCD高度。
 8  * @param {app_type_t} app_type 應用程序的類型。
 9  * @param {const char*} app_name 應用程序的名稱(必須為常量字符串)。
10  * @param {const char*} app_root 應用程序的根目錄,用於定位資源文件(必須為常量字符串)。
11  *
12  * @return {ret_t} 返回RET_OK表示成功,否則表示失敗。
13  */
14 ret_t tk_init(wh_t w, wh_t h, app_type_t app_type, const char* app_name, const char* app_root);
 看到上面的注釋,我想大家都應該明白了,每個 AWTK 的程序都必須最先調用這個函數,包括嵌入式平台也是,這個函數會初始化平台信息,創建主循環,初始化各種控件創建信息等。

   如果在嵌入式平台中,LCD 的屏幕寬高就是這里的 LCD 寬高。

2. app_main.c 文件的 assets_manager_preload 函數

 1 /**
 2  * @method assets_manager_preload
 3  * 從文件系統中加載指定的資源,並緩存到內存中。在定義了宏WITH\_FS\_RES時才生效。
 4  * @param {assets_manager_t*} am asset manager對象。
 5  * @param {asset_type_t} type 資源的類型。
 6  * @param {char*} name 資源的名稱。
 7  *
 8  * @return {ret_t} 返回RET_OK表示成功,否則表示失敗。
 9  */
10 ret_t assets_manager_preload(assets_manager_t* am, asset_type_t type, const char* name);
 這個函數是先把資源加載到資源列表中,主要是加載默認字體和默認風格,然后等待 tk_init_assets 函數的調用,把默認的字體和風格掛載到對應的地方。

3. app_main.c 文件的 tk_init_assets 函數

  

1 /**
2  * @method tk_init_assets
3  * 初始化資源。
4  * @annotation ["private"]
5  *
6  * @return {ret_t} 返回RET_OK表示成功,否則表示失敗。
7  */
8 ret_t tk_init_assets(void);

  

這個函數主要是把默認字體和默認風格掛載到 AWTK 的 主題上面和字體管理上面,如果沒有這兩步的話,程序可能會空白一片,沒有任何東西顯示出來。

4. app_main.c 文件的 tk_run 函數

1 /**
2  * @method tk_run
3  * 進入TK事件主循環。
4  * @alias run
5  * @annotation ["static", "scriptable"]
6  *
7  * @return {ret_t} 返回RET_OK表示成功,否則表示失敗。
8  */
9 ret_t tk_run(void);
 這個函數內部是一個 UI 的主循環,不斷地繪制和觸發以及接受各種事件,AWTK 所有的函數觸發都是發生在 tk_run 函數中。

5. window_main.c 文件的 window_create 函數

/**
 * @method window_create
 * 創建window對象
 * @annotation ["constructor", "scriptable"]
 * @param {widget_t*} parent 父控件
 * @param {xy_t} x x坐標
 * @param {xy_t} y y坐標
 * @param {wh_t} w 寬度
 * @param {wh_t} h 高度
 *
 * @return {widget_t*} 對象。
 */
widget_t* window_create(widget_t* parent, xy_t x, xy_t y, wh_t w, wh_t h);
 這個函數是用來創建一個可視化的窗口,這個可視化的窗口風格類型為 window_t,目前 AWTK 創建的窗口都是全屏的,所以不需要寫入 x,y,w,h,同時因為窗口在創建的時候會默認加入 window_manager(窗口管理器)中,所以也不需要寫父控件。

注意:在AWTK 中,必須要有一個可視化的窗口,否則畫面不會刷新。

6. window_main.c 文件的 xxxxx_create 函數

 1 /**
 2  * 創建xxxxx控件對象
 3  * @param {widget_t*} parent 父控件
 4  * @param {xy_t} x x坐標
 5  * @param {xy_t} y y坐標
 6  * @param {wh_t} w 寬度
 7  * @param {wh_t} h 高度
 8  *
 9  * @return {widget_t*} 對象。
10  */
11 widget_t* xxxxx_create(widget_t* parent, xy_t x, xy_t y, wh_t w, wh_t h);
  這里 xxxxx_create 函數是泛指所有的控件創建函數,AWTK 大部分的控件創建函數都是這樣子寫,只是控件名字會代替上面的 xxxxx 就是該控件的創建函數。

   AWTK 采用樹的結構,最頂級是窗口管理器(window_manager)單例,其子集為窗口對象,窗口對象的子集為各個控件,其中每個控件都可以作為其他控件的父集,從而構成一顆 AWTK 控件大樹,如下圖:
在這里插入圖片描述

備注:

  1. 當父集被刪除后,其子集也會被刪除。
  2. AWTK 的坐標系是左上角為(0,0),從左上角到右下角,x 和 y 的值越來越大。
  3. 在 awtk\src\widgets 和 awtk\src\ext_widgets 文件夾下放在各種各樣的控件,有興趣的朋友可以去看一下。

7. window_main.c 文件的 widget_set_text 函數

/**
 * @method widget_set_text
 * 設置控件的文本。
 * 只是對widget\_set\_prop的包裝,文本的意義由子類控件決定。
 * @param {widget_t*} widget 控件對象。
 * @param {const wchar_t*}  text 文本。
 *
 * @return {ret_t} 返回RET_OK表示成功,否則表示失敗。
 */
ret_t widget_set_text(widget_t* widget, const wchar_t* text);
 該函數主要是用來設置控件的文本,因為每一個控件都會有自己的文本,但是只有部分控件會自動顯示其文本,顯示文本的常見控件有:button,label,edit,check_button等。

備注:widget_set_text 函數和 widget_set_text_utf8 函數一樣的函數,只不過是傳入的字符串類型不一樣而已。

8. window_main.c 文件的 widget_set_name 函數

/**
 * @method widget_set_name
 * 設置控件的名稱。
 * @annotation ["scriptable"]
 * @param {widget_t*} widget 控件對象。
 * @param {char*} name 名稱。
 *
 * @return {ret_t} 返回RET_OK表示成功,否則表示失敗。
 */
ret_t widget_set_name(widget_t* widget, const char* name);
 該函數主要是設置控件的名字,主要是配合查找控件的方法使用,如果不需要查找控件的話,控件的名字有沒有都無所謂。

9. window_main.c 文件的 widget_on 函數

 1 /*回調事件處理函數原型*/
 2 typedef ret_t (*event_func_t)(void* ctx, event_t* e);
 3 
 4 /**
 5  * @method widget_on
 6  * 注冊指定事件的處理函數。
 7  * @annotation ["scriptable:custom"]
 8  * @param {widget_t*} widget 控件對象。
 9  * @param {event_type_t} type 事件類型。
10  * @param {event_func_t} on_event 事件處理函數。
11  * @param {void*} ctx 事件處理函數上下文。
12  * 
13  * @return {int32_t} 返回id,用於widget_off。
14  */
15 int32_t widget_on(widget_t* widget, uint32_t type, event_func_t on_event, void* ctx);
 該函數主要是設置事件回調函數,widget_on 函數是一個很重要的函數,后面會經常使用的來設置各種事件的觸發回調函數,例如常見的事件類型有:鼠標點擊事件(EVT_CLICK),鍵盤按下事件(EVT_KEY_DOWN),長按按鈕事件(EVT_LONG_PRESS)等等,具體可以查 awtk\src\tkc\event.h 中的事件枚舉。

 例如當用戶注冊了鼠標點擊事件的回調函數后,如下代碼把 on_dec_click 函數注冊為 dec 按鈕的點擊回調函數,當鼠標點擊這個 dec 按鈕后,就會觸發 on_dec_click 函數,同時會把 widget_on 函數的第四個參數(下面的代碼的第四個參數是設置 NULL)作為 on_dec_click 函數的第一個參數傳入到 on_dec_click 函數中。

1 /* 創建遞減按鈕 */
2 widget_t* dec_btn = button_create(win, 160, 288, 160, 40);
3 widget_set_text(dec_btn, L"dec");
4 widget_on(dec_btn, EVT_CLICK, on_dec_click, NULL);

五,總結

   本文介紹的是采用 C 語言直接簡單 UI demo,希望大家可以看完后可以自己去寫一個簡單的 demo,因為 AWTK 支持采用 XML 來表述 UI 界面,所以在下一章節會用 XML 來寫和本文相同的 UI demo。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM