通常, 控制台程序在执行一个漫长的任务时,需要实时显示当前进度信息, 本文演示了类似GUI进度条控件的实现.
由于需要实时更新进度条信息,并且是要在同一行显示,所以需要用到回车转义字符'\r'.
首先是进度条结构体的定义:
- #include <unistd.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <pthread.h>
- #include <time.h>
- #include <errno.h>
- #include <sys/select.h>
- #include <sys/time.h>
- #include <sys/types.h>
-
- #define bool unsigned char
- #define true 1
- #define false 0
-
- struct progress_bar_info {
- const char *name;
- bool interactive;
- long init_length;
- long step; //步长
- long total_length;
- int width;
- char *buffer;
- };
接着是几个帮助函数, 主要是获取控制台屏幕大小:
- static int
- get_screen_width(void)
- {
- int screen_width = getenv("COLUMNS");
- return screen_width;
- }
-
- static int
- get_screen_height(void)
- {
- int screen_height = getenv("LINES");
- return screen_height;
- }
接下来是创建和显示进度条图形的函数:
- static void
- create_image(struct progress_bar_info *pbi, bool done)
- {
- char *p = pbi->buffer;
- pbi->init_length += pbi->step;
- long current_size = pbi->init_length;
- int percentage = 0;
- int i = 0;
-
- if (current_size >= pbi->total_length) {
- percentage = 100;
- strcpy(p, "100%");
- } else {
- percentage = 100.0 * current_size / pbi->total_length;
-
- sprintf (p, "%2d%% ", percentage);
- }
-
- p += 4;
-
- int progress_size = 10; //->
- *p++ = '[';
- int current_progress_size = progress_size * percentage/100;
- for (i = 0; i < progress_size; i++) {
- if (i < current_progress_size)
- *p++ = '>';
- else
- *p++ = ' ';
- }
-
- *p++ = ']';
-
- if (done)
- strcpy(p, " Done!\n");
-
- p = strchr(p, '\0'); //move to the end;
-
- }
-
- static void
- display_image(char *buf)
- {
- fprintf(stderr, "\r");//回车, 将光标设置到行的开头
- fprintf(stderr, "%s", buf);
- }
下面是创建进度条函数: 传入参数: 进度条初始长度和总长度
- void* progress_bar_create(int initial, int total)
- {
- struct progress_bar_info *pbi = (struct progress_bar_info*)malloc(sizeof(struct progress_bar_info));
-
- if (pbi == NULL)
- return NULL;
-
- if (initial > total)
- total = initial;
-
- pbi->init_length = initial;
- pbi->total_length = total;
- pbi->step = 2;
-
- pbi->width = get_screen_width() - 1; //dont't use the last screen column
- pbi->buffer = (char*)malloc(pbi->width + 100);
-
- create_image(pbi, false);
- display_image(pbi->buffer);
-
- return pbi;
- }
进度条更新:
- void progress_bar_update(void *progress_bar)
- {
- struct progress_bar_info *pbi = (struct progress_bar_info*)progress_bar;
- pbi->init_length += pbi->step;
-
- create_image (pbi, false);
- display_image (pbi->buffer);
- }
进度条完成:
- void progress_bar_finish(void *progress_bar)
- {
- struct progress_bar_info *pbi = (struct progress_bar_info*)progress_bar;
-
- create_image (pbi, true);
- display_image (pbi->buffer);
-
- free(pbi->buffer);
- }
下面是进度条测试代码:
- int mySleep(unsigned int sleepSecond)
- {
- struct timeval t_timeval;
- t_timeval.tv_sec = sleepSecond;
- t_timeval.tv_usec = 0;
- select( 0, NULL, NULL, NULL, &t_timeval );
- return 0;
- }
-
-
- void thread_func(void *data)
- {
- struct progress_bar_info *pbi = (struct progress_bar_info*)data;
-
- while (pbi->init_length < pbi->total_length) {
- progress_bar_update(pbi);
- mySleep(1);
- }
- progress_bar_finish(pbi);
-
- #if 0
- int i = 0;
- for (i = 0; i < 10; i++) {
- fprintf(stderr, "%d\r", i);
- // printf(stderr, "\r");
- mySleep(1);
- }
- fprintf(stderr, "\n");
- #endif
- }
-
- int main(void)
- {
- struct progress_bar_info *pbi = progress_bar_create(0, 100);
-
- pthread_t a_thread;
- pthread_create(&a_thread, NULL, thread_func, pbi);
-
- pthread_join(a_thread, NULL);
-
- return 0;
- }