不爱胡萝卜的仓鼠 发表于 2023-12-24 21:01

[STM32MP135F-DK]测评 ⑧使用GTK制作一个时钟+天气预报demo

<div class='showpostmsg'> 本帖最后由 不爱胡萝卜的仓鼠 于 2023-12-26 13:32 编辑

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;之前跑helloWorld的时候绘制UI使用的就是GTK,那么我这次也使用GTK,本来想在帖子中记录一下我一步一步探索的情况,但是实际情况太复杂,这个GTK我是第一次上手,兜兜转转搞了搞几天,全写下来的话内容是在太多了,我这边只能简单讲一下demo的基本情况,最终我会把demo的源码贴出来,具体的请大家自行阅读代码。</p>

<p>&nbsp; &nbsp; &nbsp; &nbsp;</p>

<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在之前的几个帖子中,我已经掌握了基础的编译文件、下载运行代码、自动更新时间、获取天气信息等,基本上已经满足了制作demo的条件,现在就差学习一下如何使用GTK绘制界面了。(当然还有使用curl库从HTTPS下载天气的json数据、解析天气json数据。但是这两个我觉得还是很快可以上手的)</p>

<p>&nbsp; &nbsp; &nbsp; &nbsp; 首先要去找一下GTK的官方文档,官网网址我放一下:<a href="https://www.gtk.org/" target="_blank">https://www.gtk.org</a>,我需要其中的API文档,连接如下:<a href="https://docs.gtk.org/gtk3/" target="_blank">https://docs.gtk.org/gtk3/</a>,这里面有快速上手的说明文档还有API说明。</p>

<p>&nbsp; &nbsp; &nbsp; &nbsp; 说一下目标:全屏显示,显示当前年月日时分秒、显示天气预报(因为之前申请的是免费无限制接口,只能获取当前天气,反正大致意思到了就行)、一个刷新按钮,(用于手动刷新天气信息,当然代码也会定时自动刷新)、一个退出按钮(用于退出整个程序,因为全屏了,没有右上角的X了)</p>

<p>&nbsp; &nbsp; &nbsp; &nbsp; 整个C代码如下</p>

<pre>
<code>#include &lt;gtk/gtk.h&gt;
#include &lt;glib.h&gt;
#include &lt;stdio.h&gt;
#include &lt;time.h&gt;
#include &lt;curl/curl.h&gt;
#include &lt;json-c/json.h&gt;

#define FULLSCREEN (1)

/* 界面布局是否从UI文件加载 */
#define LOAD_UI_FROM_FILE (1)

#if !LOAD_UI_FROM_FILE
#define BOX (1)
#define GRID (2)
#define BOX_OR_GRID (BOX)
#endif

char weather_URL[] = "请求天气预报的https连接";
char outfilename[] = "weatherData.json";

void get_weather_data_and_refresh(GtkWidget *widget);

/* 更新时间的回调函数 */
gboolean update_time(GtkWidget *widget)
{
time_t current_time;
struct tm *time_info;
char time_string;

current_time = time(NULL);
time_info = localtime(&amp;current_time);
strftime(time_string, sizeof(time_string), "%Y-%m-%d %H:%M:%S", time_info);

gtk_label_set_text(GTK_LABEL(widget), time_string);

/* 返回true,继续执行定时器 */
return TRUE;
}

/* 首次运行更新天气 */
gboolean first_run_update_weather(GtkWidget *widget)
{
get_weather_data_and_refresh(widget);
/* 返回FALSE,不再继续执行定时器 */
return FALSE;
}

/* 更新天气 */
gboolean update_weather(GtkWidget *widget)
{
get_weather_data_and_refresh(widget);
/* 返回true,继续执行定时器 */
return TRUE;
}

/* 天气代码转英文字符 */
void weather_code_to_english_text(int code, char *text)
{
switch(code)
{
    case 0:
    {
      strcpy(text, "Sunny");
    }
    break;
    case 1:
    {
      strcpy(text, "Clear");
    }
    break;
    case 2:
    {
      strcpy(text, "Fair");
    }
    break;
    case 3:
    {
      strcpy(text, "Fair");
    }
    break;
    case 4:
    {
      strcpy(text, "Cloudy");
    }
    break;
    case 5:
    {
      strcpy(text, "Partly Cloudy");
    }
    break;
    case 6:
    {
      strcpy(text, "Partly Cloudy");
    }
    break;
    case 7:
    {
      strcpy(text, "Mostly Cloudy");
    }
    break;
    case 8:
    {
      strcpy(text, "Mostly Cloudy");
    }
    break;
    case 9:
    {
      strcpy(text, "Overcast");
    }
    break;
    case 10:
    {
      strcpy(text, "Shower");
    }
    break;
    case 11:
    {
      strcpy(text, "Thundershower");
    }
    break;
    case 12:
    {
      strcpy(text, "Thundershower with Hail");
    }
    break;
    case 13:
    {
      strcpy(text, "Light Rain");
    }
    break;
    case 14:
    {
      strcpy(text, "Moderate Rain        ");
    }
    break;
    case 15:
    {
      strcpy(text, "Heavy Rain");
    }
    break;
    case 16:
    {
      strcpy(text, "Storm");
    }
    break;
    case 17:
    {
      strcpy(text, "Heavy Storm");
    }
    break;
    case 18:
    {
      strcpy(text, "Severe Storm");
    }
    break;
    case 19:
    {
      strcpy(text, "Ice Rain");
    }
    break;
    case 20:
    {
      strcpy(text, "Sleet");
    }
    break;
    case 21:
    {
      strcpy(text, "Snow Flurry        ");
    }
    break;
    case 22:
    {
      strcpy(text, "Light Snow");
    }
    break;
    case 23:
    {
      strcpy(text, "Moderate Snow");
    }
    break;
    case 24:
    {
      strcpy(text, "Heavy Snow");
    }
    break;
    case 25:
    {
      strcpy(text, "Snowstorm");
    }
    break;
    case 26:
    {
      strcpy(text, "Dust");
    }
    break;
    case 27:
    {
      strcpy(text, "Sand");
    }
    break;
    case 28:
    {
      strcpy(text, "Duststorm");
    }
    break;
    case 29:
    {
      strcpy(text, "Sandstorm");
    }
    break;
    case 30:
    {
      strcpy(text, "Foggy");
    }
    break;
    case 31:
    {
      strcpy(text, "Haze");
    }
    break;
    case 32:
    {
      strcpy(text, "Windy");
    }
    break;
    case 33:
    {
      strcpy(text, "Blustery");
    }
    break;
    case 34:
    {
      strcpy(text, "Hurricane");
    }
    break;
    case 35:
    {
      strcpy(text, "Tropical Storm");
    }
    break;
    case 36:
    {
      strcpy(text, "Tornado");
    }
    break;
    case 37:
    {
      strcpy(text, "Cold");
    }
    break;
    case 38:
    {
      strcpy(text, "Hot");
    }
    break;
    default:
    {
      strcpy(text, "Unknown");
    }
    break;
}
}

/* 解析天气json数据,如果成功填充字符串 */
int parse_weather_json_data(char *weather_data)
{
char weather_text;

/* 从文件中解析JSON数据 */
json_object *parsed_json = json_object_from_file(outfilename);


/* 如果JSON解析失败 */
if (parsed_json == NULL)
{
    printf("failed to parse weather json data\n");
    sprintf(weather_data, "failed to parse weather data");
}
/* 解析成功 */
else
{
    printf("success to parse JSON data\n");
    // printf("weather json data: %s\n", json_object_to_json_string(parsed_json));

    /* 找第一层results */
    json_object *results = json_object_object_get(parsed_json, "results");
    // printf("results: %s\n", json_object_get_string(results));
    if (results != NULL)
    {
      for (int i = 0; i &lt; json_object_array_length(results); i++)
      {
      /* results的数组的第i个 */
      json_object *results_obj = json_object_array_get_idx(results, i);

      /* 找到第二层的now */
      json_object *now = json_object_object_get(results_obj, "now");
      if (now != NULL)
      {
          /* 得到天气的code和温度数据 */
          int code = json_object_get_int(json_object_object_get(now, "code"));
          const char *temperature = json_object_get_string(json_object_object_get(now, "temperature"));
          // printf("code: %d\n", code);
          // printf("temperature: %s\n", temperature);
         
          /* 天气code转成英文字符 */
          weather_code_to_english_text(code, weather_text);
          /* 组装整个天气的字符串 */
          sprintf(weather_data, "now weather: %s    now temperature: %s\n", weather_text, temperature);
      }
      else
      {
          printf("can't find now\n");
      }
      }
    }
    else
    {
      printf("can't find results\n");
    }

    /* 释放JSON对象内存 */
    json_object_put(parsed_json);
    printf("finish json parse\n");
}
return 0;
}

/* 回调函数,用于获取HTTP响应码 */
static int check_response(void *clientp, curl_infotype type, char *buffer, size_t size)
{
/* 暂时不需要响应码,屏蔽掉 */
#if 0
int response_code = 0;
if (type == CURLINFO_RESPONSE_CODE)
{
      response_code = *((int *)clientp);
}
#endif
return 0;
}

/* 下载天气的json数据 */
int download_weather_json_data(void)
{
CURL *curl;
CURLcode res;
FILE *fp;

curl = curl_easy_init();
if (curl)
{
    fp = fopen(outfilename, "wb");
    if (!fp)
    {
      printf("Error opening file\n");
      return 1;
    }

    curl_easy_setopt(curl, CURLOPT_URL, weather_URL);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL); // 使用默认的写入函数
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); // 将文件指针传递给写入函数
    curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, NULL); // 不需要处理头部信息
    curl_easy_setopt(curl, CURLOPT_HEADERDATA, NULL); // 不需要传递头部数据
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); // 不验证SSL证书(不安全,仅用于演示)
    curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, check_response); // 设置调试回调函数,用于获取HTTP响应码
    res = curl_easy_perform(curl); // 发送请求并下载文件
    if (res != CURLE_OK)
    {
      printf("curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
      fclose(fp); // 关闭文件指针
      return 1;
    }
    else
    {
      printf("success download weather json data\n");
      fclose(fp); // 关闭文件指针。
    }

    curl_easy_cleanup(curl); // 清理资源
}
else
{
    printf("curl_easy_init() failed\n");
    return 1;
}
return 0;
}

/* 获取天气数据并刷新 */
void get_weather_data_and_refresh(GtkWidget *widget)
{
char weather_string;
int result;

/* 获取天气JSON数据 */
result = download_weather_json_data();

/* 解析天气JSON数据 */
if (result == 1)
{
    /* 下载天气json数据失败,显示对应的文本 */
    sprintf(weather_string, "fail to download weather data");
    gtk_label_set_text(GTK_LABEL(widget), weather_string);
}
else
{
    sprintf(weather_string, "success to download weather data");
    parse_weather_json_data(weather_string);
    gtk_label_set_text(GTK_LABEL(widget), weather_string);
}
}

/* 刷新按钮回调函数,按钮被按下时调用*/
void button_refresh_clicked_cb(GtkButton *button, GtkWidget *widget)
{
gtk_label_set_text(GTK_LABEL(widget), "get weather data ......");
get_weather_data_and_refresh(widget);
}

/* mian函数 */
int main(int argc, char *argv[])
{
/* 初始化GTK */
gtk_init(&amp;argc, &amp;argv);

#if LOAD_UI_FROM_FILE
/* 从ui文件加载界面布局 */
GtkBuilder *builder = gtk_builder_new();
GError *error = NULL;
gtk_builder_add_from_file (builder, "/usr/local/ui.ui", &amp;error);
if (error != NULL)
{
    printf("error: fail to load UI file: %s\n", error-&gt;message);
    g_error_free(error);
    return 1;
}
#endif

/* 创建窗口 */
#if LOAD_UI_FROM_FILE
GtkWidget *window = GTK_WIDGET(gtk_builder_get_object(builder, "window"));
if (window == NULL)
{
    printf("error: get object window fail\n");
}
#else
GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
#endif
/* 设置窗口标签 */
gtk_window_set_title (GTK_WINDOW (window), "Clock and Weather Forecast");
/* 设置窗口的最小大小 */
gtk_widget_set_size_request(window, 400, 250);
/* 绑定窗口的X,可以从触摸屏关闭整个程序 */
g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL);

#if !LOAD_UI_FROM_FILE
#if (BOX_OR_GRID == BOX)
/* 创建一个box,后续的控件都放box中 */
GtkWidget *box1 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 20);
/* 把box放到window中 */
gtk_container_add(GTK_CONTAINER(window), box1);
#elif (BOX_OR_GRID == GRID)
/* 创建一个grid,后续的控件都放grid中 */
GtkWidget *grid = gtk_grid_new ();
/* 把grid放到window中 */
gtk_container_add (GTK_CONTAINER (window), grid);
#endif
#endif

/* 创建一个标签,用于显示时间 */
#if LOAD_UI_FROM_FILE
GtkWidget *label_timer = GTK_WIDGET(gtk_builder_get_object(builder, "label_timer"));
#else
GtkWidget *label_timer = gtk_label_new("waiting for get sys time");
#if (BOX_OR_GRID == BOX)
/* 把标签放进box中 */
gtk_box_pack_start(GTK_BOX(box1), label_timer, FALSE, FALSE, 0);
#elif (BOX_OR_GRID == GRID)
/* 把标签放进grid中 */
gtk_grid_attach (GTK_GRID (grid), label_timer, 0, 0, 3, 1);
#endif
#endif

/* 创建一个标签,用于天气信息 */
#if LOAD_UI_FROM_FILE
GtkWidget *label_weather = GTK_WIDGET(gtk_builder_get_object(builder, "label_weather"));
#else
GtkWidget *label_weather = gtk_label_new("waiting for get weather forecast information");
#if (BOX_OR_GRID == BOX)
/* 把标签放进box中 */
gtk_box_pack_start(GTK_BOX(box1), label_weather, FALSE, FALSE, 0);
#elif (BOX_OR_GRID == GRID)
/* 把标签放进grid中 */
gtk_grid_attach (GTK_GRID (grid), label_weather, 0, 1, 1, 1);
#endif
#endif

/* 创建一个button,用于手动刷新天气信息 */
#if LOAD_UI_FROM_FILE
GtkWidget *button_refresh = GTK_WIDGET(gtk_builder_get_object(builder, "button_refresh"));
#else
GtkWidget *button_refresh = gtk_button_new_with_label(refresh);
// gtk_widget_set_size_request(button_refresh, 50, 20);
#if (BOX_OR_GRID == BOX)
/* 把button放进box中 */
gtk_box_pack_start(GTK_BOX(box1), button_refresh, FALSE, FALSE, 0);
#elif (BOX_OR_GRID == GRID)
/* 把标签放进grid中 */
gtk_grid_attach (GTK_GRID (grid), button_refresh, 3, 1, 1, 1);
#endif
#endif
/* 设置回调函数 */
g_signal_connect(button_refresh, "clicked", G_CALLBACK(button_refresh_clicked_cb), label_weather);

/* 创建一个button,用于退出程序 */
#if LOAD_UI_FROM_FILE
GtkWidget *button_quit = GTK_WIDGET(gtk_builder_get_object(builder, "button_quit"));
#else
GtkWidget *button_quit = gtk_button_new_with_label("quit");
// gtk_widget_set_size_request(button_quit, 50, 20);
#if (BOX_OR_GRID == BOX)
/* 把button放进box中 */
gtk_box_pack_start(GTK_BOX(box1), button_quit, FALSE, FALSE, 0);
#elif (BOX_OR_GRID == GRID)
/* 把标签放进grid中 */
gtk_grid_attach (GTK_GRID (grid), button_quit, 3, 3, 1, 1);
#endif
#endif
/* 设置回调函数 */
g_signal_connect(button_quit, "clicked", G_CALLBACK (gtk_main_quit), NULL);


/* 把刚才创建的窗口显示出来 */
gtk_widget_show_all(window);
#if FULLSCREEN
/* 全屏显示 */
gtk_window_fullscreen(GTK_WINDOW(window));
#endif

/* 创建定时器,每隔1s刷新时间 */
g_timeout_add(1000, (GSourceFunc)update_time, label_timer);

/* 创建定时器,等时间到了,不再运行定时器。用于首次上电后更新天气数据。如果在这里直接更新,一旦下载或解析有问题,GTK界面会出来的很慢 */
g_timeout_add(1000, (GSourceFunc)first_run_update_weather, label_weather);

/* 创建定时器,30min定时更新一次天气数据 */
g_timeout_add(1800000, (GSourceFunc)update_weather, label_weather);


gtk_main();
#if LOAD_UI_FROM_FILE
g_object_unref(builder);
#endif

return 0;
}
</code></pre>

<p>配套的Makefile</p>

<pre>
<code>PROG = gtk_time
SRCS = gtk_time.c

CLEANFILES = $(PROG)

# Add / change option in CFLAGS and LDFLAGS
CFLAGS += -Wall $(shell pkg-config --cflags gtk+-3.0)
LDFLAGS += $(shell pkg-config --libs gtk+-3.0)

CFLAGS += -I/path/to/curl/include
LDFLAGS += -L/path/to/curl/lib -lcurl

LDFLAGS += -L/path/to/json-c/lib -ljson-c

all: $(PROG)

$(PROG): $(SRCS)
        $(CC) -o $@ $^ $(CFLAGS) $(LDFLAGS)

clean:
        rm -f $(CLEANFILES) $(patsubst %.c,%.o, $(SRCS))</code></pre>

<p>对应的ui.ui文件代码</p>

<pre>
<code>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;!-- Generated with glade 3.22.2 --&gt;
&lt;interface&gt;
&lt;requires lib="gtk+" version="3.20"/&gt;
&lt;object class="GtkWindow" id="window"&gt;
    &lt;property name="can_focus"&gt;False&lt;/property&gt;
    &lt;child type="titlebar"&gt;
      &lt;placeholder/&gt;
    &lt;/child&gt;
    &lt;child&gt;
      &lt;object class="GtkBox"&gt;
      &lt;property name="visible"&gt;True&lt;/property&gt;
      &lt;property name="can_focus"&gt;False&lt;/property&gt;
      &lt;property name="orientation"&gt;vertical&lt;/property&gt;
      &lt;child&gt;
          &lt;object class="GtkBox"&gt;
            &lt;property name="visible"&gt;True&lt;/property&gt;
            &lt;property name="can_focus"&gt;False&lt;/property&gt;
            &lt;property name="orientation"&gt;vertical&lt;/property&gt;
            &lt;child&gt;
            &lt;object class="GtkLabel" id="label_timer"&gt;
                &lt;property name="visible"&gt;True&lt;/property&gt;
                &lt;property name="can_focus"&gt;False&lt;/property&gt;
                &lt;property name="label" translatable="yes"&gt;waiting for get sys time&lt;/property&gt;
            &lt;/object&gt;
            &lt;packing&gt;
                &lt;property name="expand"&gt;False&lt;/property&gt;
                &lt;property name="fill"&gt;True&lt;/property&gt;
                &lt;property name="position"&gt;0&lt;/property&gt;
            &lt;/packing&gt;
            &lt;/child&gt;
          &lt;/object&gt;
          &lt;packing&gt;
            &lt;property name="expand"&gt;False&lt;/property&gt;
            &lt;property name="fill"&gt;True&lt;/property&gt;
            &lt;property name="padding"&gt;32&lt;/property&gt;
            &lt;property name="position"&gt;0&lt;/property&gt;
          &lt;/packing&gt;
      &lt;/child&gt;
      &lt;child&gt;
          &lt;object class="GtkBox"&gt;
            &lt;property name="visible"&gt;True&lt;/property&gt;
            &lt;property name="can_focus"&gt;False&lt;/property&gt;
            &lt;child&gt;
            &lt;object class="GtkLabel" id="label_weather"&gt;
                &lt;property name="visible"&gt;True&lt;/property&gt;
                &lt;property name="can_focus"&gt;False&lt;/property&gt;
                &lt;property name="halign"&gt;center&lt;/property&gt;
                &lt;property name="valign"&gt;center&lt;/property&gt;
                &lt;property name="margin_left"&gt;38&lt;/property&gt;
                &lt;property name="label" translatable="yes"&gt;waiting for get weather forecast information&lt;/property&gt;
                &lt;property name="justify"&gt;center&lt;/property&gt;
            &lt;/object&gt;
            &lt;packing&gt;
                &lt;property name="expand"&gt;False&lt;/property&gt;
                &lt;property name="fill"&gt;True&lt;/property&gt;
                &lt;property name="position"&gt;0&lt;/property&gt;
            &lt;/packing&gt;
            &lt;/child&gt;
            &lt;child&gt;
            &lt;object class="GtkButton" id="button_refresh"&gt;
                &lt;property name="label" translatable="yes"&gt;refresh&lt;/property&gt;
                &lt;property name="visible"&gt;True&lt;/property&gt;
                &lt;property name="can_focus"&gt;True&lt;/property&gt;
                &lt;property name="receives_default"&gt;True&lt;/property&gt;
            &lt;/object&gt;
            &lt;packing&gt;
                &lt;property name="expand"&gt;False&lt;/property&gt;
                &lt;property name="fill"&gt;True&lt;/property&gt;
                &lt;property name="pack_type"&gt;end&lt;/property&gt;
                &lt;property name="position"&gt;1&lt;/property&gt;
            &lt;/packing&gt;
            &lt;/child&gt;
          &lt;/object&gt;
          &lt;packing&gt;
            &lt;property name="expand"&gt;False&lt;/property&gt;
            &lt;property name="fill"&gt;True&lt;/property&gt;
            &lt;property name="padding"&gt;41&lt;/property&gt;
            &lt;property name="position"&gt;1&lt;/property&gt;
          &lt;/packing&gt;
      &lt;/child&gt;
      &lt;child&gt;
          &lt;object class="GtkBox"&gt;
            &lt;property name="visible"&gt;True&lt;/property&gt;
            &lt;property name="can_focus"&gt;False&lt;/property&gt;
            &lt;property name="orientation"&gt;vertical&lt;/property&gt;
            &lt;child&gt;
            &lt;object class="GtkButton" id="button_quit"&gt;
                &lt;property name="label" translatable="yes"&gt;quit&lt;/property&gt;
                &lt;property name="visible"&gt;True&lt;/property&gt;
                &lt;property name="can_focus"&gt;True&lt;/property&gt;
                &lt;property name="receives_default"&gt;True&lt;/property&gt;
                &lt;property name="margin_left"&gt;170&lt;/property&gt;
                &lt;property name="margin_right"&gt;170&lt;/property&gt;
            &lt;/object&gt;
            &lt;packing&gt;
                &lt;property name="expand"&gt;False&lt;/property&gt;
                &lt;property name="fill"&gt;True&lt;/property&gt;
                &lt;property name="position"&gt;0&lt;/property&gt;
            &lt;/packing&gt;
            &lt;/child&gt;
          &lt;/object&gt;
          &lt;packing&gt;
            &lt;property name="expand"&gt;False&lt;/property&gt;
            &lt;property name="fill"&gt;True&lt;/property&gt;
            &lt;property name="pack_type"&gt;end&lt;/property&gt;
            &lt;property name="position"&gt;2&lt;/property&gt;
          &lt;/packing&gt;
      &lt;/child&gt;
      &lt;/object&gt;
    &lt;/child&gt;
&lt;/object&gt;
&lt;/interface&gt;
</code></pre>

<p>界面显示如下</p>

<div style="text-align: center;"></div>

<p>&nbsp;</p>

<p>这个代码写的界面有以下几种配置方式</p>

<p>①是否全屏显示,修改FULLSCREEN宏定义</p>

<p>②界面布局是否从UI文件加载,如果是,那么就需要把上面的ui.ui文件也放到135中,我代码中写的路径是/usr/local/。修改LOAD_UI_FROM_FILE宏定义</p>

<p>③不使用UI文件的情况下,使用BOX布局还是GRID布局,修改BOX_OR_GRID。这是我前期探索时,手动制作的界面布局</p>

<p>&nbsp;</p>

<p>最终版的配置参数是全屏+使用UI加载</p>

<p>&nbsp;</p>

<p>详细的代码我就不解读了,我写了非常详细的注释。</p>

<p>&nbsp;</p>

<p>运行demo步骤如下,在ubuntu中编译好代码,将执行文件和ui.ui放到135的/usr/local下,修改两个文件的权限,然后运行</p>

<pre>
<code>chmod 777 ui.ui

chmod 777 gtk_time

su -l weston -c "/usr/local/gtk_time"</code></pre>

<p>&nbsp;</p>

<p>我这里要说一下ui.ui文件是怎么来的,手动绘制界面真的非常麻烦,效率也很低,我在网上搜索到一个好东西&ldquo;Glade&rdquo;,他只管绘制界面,有点类似前端后端分离的感觉,他最终会生产一个文件,然后GTK加载这文件,就可以直接显示出绘制的界面,这个软件需要在ubuntu中运行(win貌似也可以,但是我没有找到对应的安装包),在ubuntu中安装只需要使用以下命令</p>

<pre>
<code>sudo apt-get install glade</code></pre>

<p>安装好后就有一个Glade应用程序的图标,点击即可运行(下图是我绘制的界面)</p>

<div style="text-align: center;"></div>

<p>这个软件我就不细讲了,大家可以自行摸索,总的来说还是很容易上手的,我大约捣鼓了十来分钟就基本上手了</p>

<p>绘制好后,点击save,默认的文件扩展名是&ldquo;.glade&rdquo;,其实就是xml,可以手动修改后缀,然后就可以使用了(给到135后还要修改一下文件的权限,保证他可以被读取)</p>

<p>&nbsp;</p>

<p>最后放一段演示视频</p>

<p>8351d3844db6f4ba7218b2422b983699<br />
&nbsp;</p>
</div><script>                                        var loginstr = '<div class="locked">查看本帖全部内容,请<a href="javascript:;"   style="color:#e60000" class="loginf">登录</a>或者<a href="https://bbs.eeworld.com.cn/member.php?mod=register_eeworld.php&action=wechat" style="color:#e60000" target="_blank">注册</a></div>';
                                       
                                        if(parseInt(discuz_uid)==0){
                                                                                                (function($){
                                                        var postHeight = getTextHeight(400);
                                                        $(".showpostmsg").html($(".showpostmsg").html());
                                                        $(".showpostmsg").after(loginstr);
                                                        $(".showpostmsg").css({height:postHeight,overflow:"hidden"});
                                                })(jQuery);
                                        }                </script><script type="text/javascript">(function(d,c){var a=d.createElement("script"),m=d.getElementsByTagName("script"),eewurl="//counter.eeworld.com.cn/pv/count/";a.src=eewurl+c;m.parentNode.insertBefore(a,m)})(document,523)</script>
页: [1]
查看完整版本: [STM32MP135F-DK]测评 ⑧使用GTK制作一个时钟+天气预报demo