通常, 控制台程序在执行一个漫长的任务时,需要实时显示当前进度信息, 本文演示了类似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;
}