sonicfirr 发表于 2022-4-26 23:04

【平头哥Sipeed LicheeRV 86 Panel测评】十五、lvgl日历控件和显示天气

<p style="text-indent:24.0000pt; text-align:justify"><span style="font-size:12pt"><span style="125%"><span style="font-family:&quot;Times New Roman&quot;"><span style="font-size:12.0000pt"><span style="font-family:宋体"><font face="宋体">本篇结合本人前两篇的</font><font face="Times New Roman">HTTP</font><font face="宋体">请求天气数据(通过&ldquo;心知天气&rdquo;网站)和</font><font face="Times New Roman">lvgl</font><font face="宋体">显示图片及时间,在案例主界面上增加了日历显示和实时天气显示,先直接上图。</font></span></span></span></span></span></p>

<p style="text-indent:24.0000pt; text-align:justify">&nbsp;</p>

<p class="imagemiddle" style="text-align: center;"></p>

<p style="text-align: center;"><span style="font-size:12pt"><span style="125%"><span style="font-family:&quot;Times New Roman&quot;"><span style="font-size:10.5000pt"><span style="font-family:宋体"><font face="宋体">图</font><font face="Times New Roman">15-1 </font><font face="宋体">日历显示效果</font></span></span></span></span></span></p>

<p align="center" style="text-align:center">&nbsp;</p>

<h2 style="text-align:justify"><span style="font-size:14pt"><span style="125%"><span style="font-family:&quot;Times New Roman&quot;"><span style="font-weight:bold"><b><span style="font-size:14.0000pt"><span style="font-family:宋体"><span style="font-weight:bold"><font face="Times New Roman">1</font><font face="宋体">、</font><font face="Times New Roman">lvgl</font><font face="宋体">日历控件</font></span></span></span></b></span></span></span></span></h2>

<p style="text-indent:24.0000pt; text-align:justify"><span style="font-size:12pt"><span style="125%"><span style="font-family:&quot;Times New Roman&quot;"><span style="font-size:12.0000pt"><span style="font-family:宋体"><font face="Times New Roman">calendar</font><font face="宋体">是</font><font face="Times New Roman">lvgl</font><font face="宋体">提供的&ldquo;</font><font face="Times New Roman">Extra widgets</font><font face="宋体">&rdquo;组件之一,需要注意的是</font><font face="Times New Roman">8.0</font><font face="宋体">版本后有几个</font><font face="Times New Roman">API</font><font face="宋体">的传参发生了变化,本例使用</font><font face="Times New Roman">8.3</font><font face="宋体">版本,设置日期是需要同时传递&ldquo;年、月、日&rdquo;三个参数。</font></span></span></span></span></span></p>

<p style="text-indent:24.0000pt; text-align:justify"><span style="font-size:12pt"><span style="125%"><span style="font-family:&quot;Times New Roman&quot;"><span style="font-size:12.0000pt"><span style="font-family:宋体"><font face="宋体">本例使用的</font><font face="Times New Roman">API</font><font face="宋体">有:</font><font face="Times New Roman">lv_calendar_create()</font><font face="宋体">、</font><font face="Times New Roman">lv_canlendar_set_today_date()</font><font face="宋体">、</font><font face="Times New Roman">lv_calendar_set_showed_date()</font><font face="宋体">和</font><font face="Times New Roman">lv_calendar_header_arrow_create()</font><font face="宋体">。</font></span></span></span></span></span></p>

<p style="text-indent:24.0000pt; text-align:justify"><span style="font-size:12pt"><span style="125%"><span style="font-family:&quot;Times New Roman&quot;"><span style="font-size:12.0000pt"><span style="font-family:宋体"><font face="Times New Roman">lv_calendar_create()</font><font face="宋体">函数用于实例化</font><font face="Times New Roman">calendar</font><font face="宋体">控件,传参是控件的父容器指针,本例使用&ldquo;</font><font face="Times New Roman">lv_scr_act()</font><font face="宋体">&rdquo;即系统屏幕。</font></span></span></span></span></span></p>

<p style="text-indent:24.0000pt; text-align:justify">&nbsp;</p>

<p class="imagemiddle" style="text-align: center;"></p>

<p style="text-align: center;"><span style="font-size:12pt"><span style="125%"><span style="font-family:&quot;Times New Roman&quot;"><span style="font-size:10.5000pt"><span style="font-family:宋体"><font face="宋体">图</font><font face="Times New Roman">15-2 </font><font face="宋体">函数</font></span></span><span style="font-size:10.5000pt"><span style="font-family:宋体"><font face="Times New Roman">lv_calendar_create()</font><font face="宋体">说明</font></span></span></span></span></span></p>

<p align="center" style="text-align:center">&nbsp;</p>

<p style="text-indent:24.0000pt; text-align:justify"><span style="font-size:12pt"><span style="125%"><span style="font-family:&quot;Times New Roman&quot;"><span style="font-size:12.0000pt"><span style="font-family:宋体"><font face="Times New Roman">lv_canlendar_set_today_date()</font><font face="宋体">函数用于设置当前日期,本人使用发现</font><font face="Times New Roman">lvgl</font><font face="宋体">是附带万年历功能的,只要设置好当天的年月日,就可以自动生成正确的日历排布。函数传参分别是控件指针和年月日数据。</font></span></span></span></span></span></p>

<p style="text-indent:24.0000pt; text-align:justify"><span style="font-size:12pt"><span style="125%"><span style="font-family:&quot;Times New Roman&quot;"><span style="font-size:12.0000pt"><span style="font-family:宋体"><font face="宋体">关于年月日参数有两点注意事项。一是</font><font face="Times New Roman">v7</font><font face="宋体">版本中,传参通过</font><font face="Times New Roman">lv_calendar_date_t</font><font face="宋体">结构体,其包含年月日三个成员。二是如果使用了</font><font face="Times New Roman">C time</font><font face="宋体">库的</font><font face="Times New Roman">struct tm</font><font face="宋体">,注意其中年份需要加上&ldquo;</font><font face="Times New Roman">1900</font><font face="宋体">&rdquo;,而月份则需要加&ldquo;</font><font face="Times New Roman">1</font><font face="宋体">&rdquo;。</font></span></span></span></span></span></p>

<p style="text-indent:24.0000pt; text-align:justify">&nbsp;</p>

<p class="imagemiddle" style="text-align: center;"></p>

<p style="text-align: center;"><span style="font-size:12pt"><span style="125%"><span style="font-family:&quot;Times New Roman&quot;"><span style="font-size:10.5000pt"><span style="font-family:宋体"><font face="宋体">图</font><font face="Times New Roman">15-3 </font><font face="宋体">函数</font><font face="Times New Roman">lv_calendar_set_today_date()</font><font face="宋体">说明</font></span></span></span></span></span></p>

<p align="center" style="text-align:center">&nbsp;</p>

<p style="text-indent:24.0000pt; text-align:justify"><span style="font-size:12pt"><span style="125%"><span style="font-family:&quot;Times New Roman&quot;"><span style="font-size:12.0000pt"><span style="font-family:宋体"><font face="Times New Roman">lv_calendar_set_showed_date()</font><font face="宋体">函数用于设置日历当前显示页,也就是设置当前月份。本人实验的效果是当天日期框会自动高亮,如果想设置多个高亮日期,可以使用函数</font><font face="Times New Roman">lv_calendar_set_highlighted_dates()</font><font face="宋体">。</font></span></span></span></span></span></p>

<p style="text-indent:24.0000pt; text-align:justify">&nbsp;</p>

<p class="imagemiddle" style="text-align: center;"></p>

<p style="text-align: center;"><span style="font-size:12pt"><span style="125%"><span style="font-family:&quot;Times New Roman&quot;"><span style="font-size:10.5000pt"><span style="font-family:宋体"><font face="宋体">图</font><font face="Times New Roman">15-4 </font><font face="宋体">函数</font><font face="Times New Roman">lv_calendar_set_showed_date()</font><font face="宋体">和</font><font face="Times New Roman">lv_calendar_set_highlighted_dates()</font><font face="宋体">说明</font></span></span></span></span></span></p>

<p align="center" style="text-align:center">&nbsp;</p>

<p style="text-indent:24.0000pt; text-align:justify"><span style="font-size:12pt"><span style="125%"><span style="font-family:&quot;Times New Roman&quot;"><span style="font-size:12.0000pt"><span style="font-family:宋体"><font face="Times New Roman">lv_calendar_header_arrow_create()</font><font face="宋体">函数用于向日历控件顶部增加&ldquo;左、右箭头&rdquo;两个按钮用于日历翻页(一页是一月)。此外,还有函数</font><font face="Times New Roman">lv_calendar_header_dropdown_create()</font><font face="宋体">则是设置两个下拉列表分别用于选择年份和月份。这两个函数都只用传递日历控件指针一个参数,且是</font><font face="Times New Roman">8.1</font><font face="宋体">版本新增</font><font face="Times New Roman">API</font><font face="宋体">。</font></span></span></span></span></span></p>

<h2 style="text-align:justify"><span style="font-size:14pt"><span style="125%"><span style="font-family:&quot;Times New Roman&quot;"><span style="font-weight:bold"><b><span style="font-size:14.0000pt"><span style="font-family:宋体"><span style="font-weight:bold"><font face="Times New Roman">2</font><font face="宋体">、日历和天气显示案例</font></span></span></span></b></span></span></span></span></h2>

<p style="text-indent:24.0000pt; text-align:justify"><span style="font-size:12pt"><span style="125%"><span style="font-family:&quot;Times New Roman&quot;"><span style="font-size:12.0000pt"><span style="font-family:宋体"><font face="宋体">本案例的思路是:</font><font face="Times New Roman">1</font><font face="宋体">)在应用启动时,获取当前时间(上篇中已经实现),然后将时间保存在全局量&ldquo;</font><font face="Times New Roman">struct tm today</font><font face="宋体">&rdquo;中,并利用变量&ldquo;</font><font face="Times New Roman">today</font><font face="宋体">&rdquo;来初始化日历控件的日期数据。</font><font face="Times New Roman">2</font><font face="宋体">)上篇实现的时间显示案例,通过</font><font face="Times New Roman">lvgl</font><font face="宋体">定时器,每秒获取本地数据,此处在定时器回调中再增加一个每到正分钟发送&ldquo;</font><font face="Times New Roman">Linux</font><font face="宋体">条件变量&rdquo;。</font><font face="Times New Roman">3</font><font face="宋体">)同时,应用启动时建立两个线程&mdash;&mdash;</font><font face="Times New Roman">lvgl</font><font face="宋体">线程和请求天气线程,请求天气线程等待条件变量到来,开启一次天气数据请求过程。</font></span></span></span></span></span></p>

<p style="text-indent:24.0000pt; text-align:justify"><span style="font-size:12pt"><span style="125%"><span style="font-family:&quot;Times New Roman&quot;"><span style="font-size:12.0000pt"><span style="font-family:宋体"><font face="宋体">本例代码结合本人</font>&ldquo;十三、利用<font face="Times New Roman">TCP</font><font face="宋体">封装</font><font face="Times New Roman">HTTP</font><font face="宋体">包请求天气信息&rdquo;和&ldquo;十四、</font><font face="Times New Roman">lvgl</font><font face="宋体">显示图片和本地时间&rdquo;两篇创建的案例,这里只给出改变部分。</font></span></span></span></span></span></p>

<p style="text-indent:24.0000pt; text-align:justify">&nbsp;</p>

<pre>
<code>/* Includes ------------------------------------------------------- */
// 增加头文件,cJSON用于解析JSON格式的天气数据
#include "cJSON.h"

/* Private macro -------------------------------------------------- */
// 增加请求天气数据相关的宏定义
#define HTTP_IP                "116.62.81.138"
#define HTTP_PORT              80
#define NOW                    "now.json"
#define API_KEY                "SK0LJ8FI2TP0L-IsQ"
#define CITY                   "tianjin"
#define REQ_PACK               "GET https://api.thinkpage.cn/v3/weather/%s?key=%s&amp;location=%s&amp;language=en&amp;unit=c\r\n\r\n"
#define N                      1024
// struct for weather data 建立结构体存储解析后的天气数据
typedef struct {
    char id;
    char name;
    char country;
    char path;
    char timezone;
    char tz_offset;
    char text;
    char code;
    char temp;
    char last_update;
} weather_t;

/* Global variables ----------------------------------------------- */
// 增加显示天气的标签控件定义
lv_obj_t *weather_label;  //pointer of weather label instance
// 增加日历控件定义
lv_obj_t  *calendar;      //pointer of calendar instance
// 定义today变量存储当前日期,用于设置日历
struct tm today;          //
// 请求天气的线程ID
pthread_t reqweather_tid; //request weather thread id
// 请求天气线程等待的条件变量(min_cond)
// Linux中需要互斥量包含条件变量的使用,所以定义cond_mutex
pthread_mutex_t cond_mutex;  //mutex for 1-min cond
pthread_cond_t  min_cond; //1-min cond
/* Private functions ---------------------------------------------- */
int main(void) {
// other code from previous demo
// main()函数中创建互斥量、条件变量、请求天气线程
//by author. create mutex for 1-min cond
    if(pthread_mutex_init(&amp;cond_mutex, NULL) != 0) {
        errlog("initialize cond mutex error");
    }

//by author. create condition for 1-min
    if(pthread_cond_init(&amp;min_cond, NULL) != 0) {
        errlog("initialize 1 minute condition error");
    }

//by author. create request weather thread
    if(pthread_create(&amp;reqweather_tid, NULL, thread_reqweather, (void *)0) != 0) {
        errlog("create request weather thread error");
    }

//by author. wait for thread exit, this demo should never be here.
    pthread_join(lvgl_tid, &amp;retval);
    printf("lvgl thread exit, return value: %s\n", (char *)retval);
    pthread_join(reqweather_tid, &amp;retval);
    printf("request weather thread exit, return value: %s\n", (char *)retval);
    pthread_mutex_destroy(&amp;lvgl_mutex);
    pthread_mutex_destroy(&amp;cond_mutex);
    pthread_cond_destroy(&amp;min_cond);
    return 0;
}

void aita_CreateMainUI(void) {
// other code from previous demo
// aita_CreateMainUI()被main()函数调用,初始化主界面。
    //by author. create the weather label
    weather_label = lv_label_create(sys_scr);
    lv_label_set_text(weather_label, "         ");
    lv_obj_align(weather_label, LV_ALIGN_TOP_LEFT, 200, 120);
    //by author. create the calendar
    calendar = lv_calendar_create(sys_scr);
    lv_obj_set_size(calendar, 235, 235);
    lv_obj_align(calendar, LV_ALIGN_BOTTOM_LEFT, 10, -50);
    lv_calendar_set_today_date(calendar, today.tm_year+1900, today.tm_mon+1, today.tm_mday);
    lv_calendar_set_showed_date(calendar, today.tm_year+1900, today.tm_mon+1);
    lv_calendar_header_arrow_create(calendar);
}

// 增加正分钟发送条件变量
void sec_timer_cb(lv_timer_t *timer) {
    aita_GetTime();
    lv_label_set_text(main_label, main_label_text);
    if(today.tm_sec == 0) {
        //by author. send condition signal per whole minute
        pthread_cond_signal(&amp;min_cond);
    }
}
// 增加对today的赋值
void aita_GetTime(void) {
    time_t    tsec;
    struct tm *tlocal;
    tsec = time(NULL);
    tlocal = localtime(&amp;tsec);
    today = *tlocal;
    memset(main_label_text, 0, 32);
    strftime(main_label_text, 32, "%Y-%m-%d %a %H:%M:%S", tlocal);
}

// 请求天气线程业务逻辑
void *thread_reqweather(void *arg) {
    int sockfd;
    struct sockaddr_in serveraddr;
    socklen_t addrlen = sizeof(serveraddr);
    char sendbuf = "";
    char recvbuf = "";
    weather_t weather = {0};
    char w_string = "";

    while(1) {
        pthread_mutex_lock(&amp;cond_mutex);
        pthread_cond_wait(&amp;min_cond, &amp;cond_mutex);
        pthread_mutex_unlock(&amp;cond_mutex);    
    //create socket
        if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) &lt; 0) {
            errlog("socket error");
        }
    //connect to server of seniverse.com
        serveraddr.sin_family = AF_INET;
        serveraddr.sin_addr.s_addr = inet_addr(HTTP_IP);
        serveraddr.sin_port = htons(HTTP_PORT);
        if((connect(sockfd, (struct sockaddr*)&amp;serveraddr, addrlen)) &lt; 0) {
            errlog("connect error");
        }
    //build &amp; send request package
        memset(sendbuf, 0, N);
        sprintf(sendbuf, REQ_PACK, NOW, API_KEY, CITY);
        if(send(sockfd, sendbuf, N, 0) &lt; 0) {
            errlog("send error");
        }
    //waiting server response
        if(recv(sockfd, recvbuf, N, 0) &lt; 0) {
            errlog("recv error");
        }
        printf("recv: %s\n", recvbuf);
    //parse &amp; print data,下面两个函数来自于“十三”案例
        aita_ParseJsonNow(recvbuf, &amp;weather);
        aita_PrintWeather(&amp;weather);
        close(sockfd);
        memset(recvbuf, 0, N);
    //show weather string
        memset(w_string, 0, 64);
        sprintf(w_string, "weather:%s temperatur:%s", weather.text, weather.temp);
        pthread_mutex_lock(&amp;lvgl_mutex);
        lv_label_set_text(weather_label, w_string);
        pthread_mutex_unlock(&amp;lvgl_mutex);      
    }
}</code></pre>

<p style="text-indent:24.0000pt; text-align:justify">&nbsp;</p>

<p style="text-indent:24.0000pt; text-align:justify"><span style="font-size:12pt"><span style="125%"><span style="font-family:&quot;Times New Roman&quot;"><span style="font-size:12.0000pt"><span style="font-family:宋体"><font face="宋体">另外,本例在</font><font face="Times New Roman">lvgl</font><font face="宋体">工程中增加了</font><font face="Times New Roman">cJSON.c</font><font face="宋体">和</font><font face="Times New Roman">cJSON.h</font><font face="宋体">文件,</font><font face="Times New Roman">Makefile</font><font face="宋体">也做出了调整,具体如下所示。</font></span></span></span></span></span></p>

<p style="text-indent:24.0000pt; text-align:justify">&nbsp;</p>

<p class="imagemiddle" style="text-align: center;"></p>

<p style="text-align: center;"><span style="font-size:12pt"><span style="125%"><span style="font-family:&quot;Times New Roman&quot;"><span style="font-size:10.5000pt"><span style="font-family:宋体"><font face="宋体">图</font><font face="Times New Roman">15-5 Makefile</font><font face="宋体">的修改</font></span></span></span></span></span></p>

<p align="center" style="text-align:center">&nbsp;</p>

<p style="text-indent:24.0000pt; text-align:justify">&nbsp;</p>

lugl4313820 发表于 2022-4-27 13:35

<p>这个界面做得很美,给你点个赞赞,专门UI设计的吗?</p>

sonicfirr 发表于 2022-4-27 18:46

lugl4313820 发表于 2022-4-27 13:35
这个界面做得很美,给你点个赞赞,专门UI设计的吗?

<p>谢谢,我是大学老师,计算机专业嵌入式方向教学的,真没想到还有人夸我的审美。本人纯理工宅,美学修养负值。<img height="52" src="https://bbs.eeworld.com.cn/static/editor/plugins/hkemoji/sticker/facebook/titter.gif" width="48" /></p>

lugl4313820 发表于 2022-4-27 21:05

sonicfirr 发表于 2022-4-27 18:46
谢谢,我是大学老师,计算机专业嵌入式方向教学的,真没想到还有人夸我的审美。本人纯理工宅,美学修养负 ...

<p>人肯定长得也帅,那肯定不差的啦,以后还得向老师多多请教!</p>

freebsder 发表于 2022-4-27 22:35

<p>这是什么开发环境,还要自己弄makefile?</p>

sonicfirr 发表于 2022-4-28 10:31

lugl4313820 发表于 2022-4-27 21:05
人肯定长得也帅,那肯定不差的啦,以后还得向老师多多请教!

<p>客气了!</p>

sonicfirr 发表于 2022-4-28 10:32

freebsder 发表于 2022-4-27 22:35
这是什么开发环境,还要自己弄makefile?

<p>VScode开发LVGL框架,用于Tina&nbsp;Linux系统,因为使用了WSL(Windows&nbsp;Linux子系统),所以可以在Win10环境做交叉编译。</p>
页: [1]
查看完整版本: 【平头哥Sipeed LicheeRV 86 Panel测评】十五、lvgl日历控件和显示天气