进度条的实现 进度条是用户界面中常见的元素,用于显示任务的完成进度。通过实现一个进度条,可以学习如何在 Linux 下进行简单的用户界面设计,同时也能理解文件链接和代码维护的重要性。结合之前讲的倒计时的代码,我们正式来实现我们 Linux 下第一个小程序 —— 进度条。结合之前讲解的 Linux 文件链接 和考虑到代码的 维护性 ,依旧采用 头文件 .h
、函数体文件 .c
、main 函数文件 .c
的三个文件进行讲解:
makefile 的配置 为了方便进度条的运行展示,这里给出我的 makefile
配置(第一种旨在理解编译流程,第二种全自动更方便):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 processBar: processBar.o gcc processBar.o -o processBar processBar.o: processBar.s gcc -c processBar.s -o processBar.o processBar.s: processBar.i gcc -S processBar.i -o processBar.s processBar.i: processBar.c gcc -E processBar.c -o processBar.i .PHONY: clean clean: rm -f processBar.i processBar.s processBar.o processBar
或者
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 TARGET = processBar SRCS = processBar.c processBarmain.c OBJS = $(SRCS:.c=.o) CC = gcc CFLAGS = -I. -Wall -O2 all: $(TARGET) $(TARGET): $(OBJS) $(CC) $(OBJS) -o $(TARGET) %.o: %.c $(CC) $(CFLAGS) -c $< -o $@ .PHONY: clean clean: rm -f processBar.i processBar.s processBar.o processBar
第一版:基础进度条 1. 单个循环打印 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include <stdio.h> #include <unistd.h> int main () { for (int i = 0 ; i <= 100 ; ++i) { printf ("\r[%3d%%]" , i); fflush (stdout); usleep (20000 ); } printf ("\n" ); return 0 ; }
问题:
输出不直观,只是一个百分比数字。
缺少进度条的视觉表示。
2. 添加进度条显示 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <stdio.h> #include <unistd.h> int main () { char bar[101 ] = {0 }; for (int i = 0 ; i <= 100 ; ++i) { bar[i] = '#' ; printf ("\r[%-100s][%3d%%]" , bar, i); fflush (stdout); usleep (100000 ); } printf ("\n" ); return 0 ; }
问题:
进度条没有动态效果,看起来比较单调。
缺少加载动画提示。
3. 改进:添加加载动画 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include <stdio.h> #include <unistd.h> const char *label = "|-\\" ; int cnt = 0 ;void processbar () { printf ("\r[%s] [%3d%%]" , &label[cnt % 3 ], cnt); fflush (stdout); cnt++; usleep (100000 ); } int main () { while (cnt <= 100 ) { processbar (); } printf ("\n" ); return 0 ; }
问题:
动画符号和进度条没有良好结合。
缺少进度条长度控制。
4. 完善进度条长度与样式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 #include <stdio.h> #include <unistd.h> #include <string.h> const char *label = "|-\\" ;char bar[101 ] = {0 }; void processbar () { static int cnt = 0 ; printf ("\r[%-100s][%3d%%][%c]" , bar, cnt, label[cnt % 3 ]); fflush (stdout); bar[cnt++] = '#' ; usleep (50000 ); } int main () { while (cnt <= 100 ) { processbar (); } printf ("\n" ); return 0 ; }
问题:
进度条结束时,右箭头没有正确显示。
变量写“死”,不利于维护。
没有体现文件之间的链接。
5. 大改文件,将变量写活,添加尾部符号“>”,优化代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 #pragma once #include <stdio.h> #include <unistd.h> #include <string.h> #define NUM 102 #define TOP 100 #define STYLE '-' #define RIGHT '>' extern void processbar (int speed) ;#include "processBar.h" const char *label = "|-\\" ;void processbar () { char bar[NUM]; memset (bar, '\0' , sizeof (bar)); int len = strlen (label); int cnt = 0 ; while (cnt <= 100 ) { printf ("[%-100s][%d%%][%c]\r" , bar, cnt, label[cnt % len]); fflush (stdout); bar[cnt++] = STYLE; bar[cnt] = '>' ; usleep (50000 ); } } #include "processBar.h" int main () { processbar (); return 0 ; }
问题
进度条完成后,尾部仍然显示“>”,显得不够美观。
命令提示行会进行覆盖。
6. 修复尾部符号“>”和提示行覆盖的问题 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 const char *label = "|-\\" ;void processbar () { char bar[NUM]; memset (bar, '\0' , sizeof (bar)); int len = strlen (label); int cnt = 0 ; while (cnt <= 100 ) { printf ("[%-100s][%d%%][%c]\r" , bar, cnt, label[cnt % len]); fflush (stdout); bar[cnt++] = STYLE; if (cnt < 100 ) { bar[cnt] = '>' ; } usleep (50000 ); } printf ("\n" ); }
7. 优化代码,提高可维护性 —— 第一版进度条诞生! 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 #pragma once #include <stdio.h> #include <unistd.h> #include <string.h> #define NUM 102 #define TOP 100 #define BODY '-' #define RIGHT '>' extern void processbar (int speed) ; #include "processBar.h" const char *label = "|-\\" ; void processbar (int speed) { char bar[NUM]; memset (bar, '\0' , sizeof (bar)); int len = strlen (label); int cnt = 0 ; while (cnt <= TOP) { printf ("[%-100s][%d%%][%c]\r" , bar, cnt, label[cnt % len]); fflush (stdout); bar[cnt++] = BODY; if (cnt < TOP) { bar[cnt] = RIGHT; } usleep (speed); } printf ("\n" ); } #include "processBar.h" int main () { processbar (50000 ); return 0 ; }
第二版:支持单任务和多任务调用 1. 单任务调用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 #pragma once #include <stdio.h> #include <unistd.h> #include <string.h> #define NUM 102 #define TOP 100 #define BODY '-' #define RIGHT '>' extern void processbar (int rate) ; #include "processBar.h" const char *label = "|-\\" ;char bar[NUM]; void processbar (int rate) { if (rate < 0 || rate > 100 ) { return ; } int len = strlen (label); printf ("[%-100s][%d%%][%c]\r" , bar, rate, label[rate % len]); fflush (stdout); bar[rate++] = BODY; if (rate < 100 ) { bar[rate] = RIGHT; } } #include "processBar.h" int main () { int total = 1000 ; int curr = 0 ; while (curr <= total) { int rate = (curr * 100 ) / total; processbar (rate); curr += 10 ; usleep (50000 ); } printf ("\n" ); return 0 ; }
2. 多任务调用: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 typedef void (*callback_t ) (int ) ;void download (callback_t cb) { int total = 1000 ; int curr = 0 ; while (curr <= total) { usleep (50000 ); int rate = curr * 100 / total; cb (rate); curr += 10 ; } printf ("\n" ); } int main () { printf ("download 1:\n" ); download (processbar); printf ("download 2:\n" ); download (processbar); printf ("download 3:\n" ); download (processbar); return 0 ; }
再次运行,会发现这样的多任务下载的进度条是同步的,不能达到真正的多任务下载,我们添加一个函数解决:
1 2 3 4 void initbar () { memset (bar,'\0' ,sizeof (bar)); }
3. 优化代码 —— 第二版进度条诞生! 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 #pragma once #include <stdio.h> #include <unistd.h> #include <string.h> #define NUM 102 #define TOP 100 #define BODY '-' #define RIGHT '>' typedef void (*callback_t ) (int ) ; void processbar (int rate) ;extern void download (callback_t cb) ; extern void initbar () ; #include "processBar.h" const char *label = "|-\\" ; char bar[NUM]; void initbar () { memset (bar, '\0' , sizeof (bar)); } void download (callback_t cb) { int total = 1000 ; int curr = 0 ; while (curr <= total) { usleep (50000 ); int rate = curr * 100 / total; cb (rate); curr += 10 ; } printf ("\n" ); } void processbar (int rate) { if (rate < 0 || rate > TOP) { fprintf (stderr, "Invalid rate: %d\n" , rate); return ; } int len = strlen (label); printf ("[%-100s][%d%%][%c]\r" , bar, rate, label[rate % len]); fflush (stdout); bar[rate++] = BODY; if (rate < TOP) { bar[rate] = RIGHT; } } #include "processBar.h" int main () { printf ("Download 1:\n" ); download (processbar); initbar (); printf ("Download 2:\n" ); download (processbar); initbar (); printf ("Download 3:\n" ); download (processbar); return 0 ; }
我们以绿色为例,添加以下内容:
1 2 #define GREEN "\033[0;32;32m" #define NONE "\033[m"
将颜色渲染到进度条,就得到了最终的彩色进度条:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 #pragma once #include <stdio.h> #include <unistd.h> #include <string.h> #define NUM 102 #define TOP 100 #define BODY '-' #define RIGHT '>' #define GREEN "\033[0;32;32m" #define NONE "\033[m" typedef void (*callback_t ) (int ) ; void processbar (int rate) ;extern void download (callback_t cb) ;extern void initbar () ; #include "processBar.h" const char *label = "|-\\" ; char bar[NUM]; void initbar () { memset (bar, '\0' , sizeof (bar)); } void download (callback_t cb) { int total = 1000 ; int curr = 0 ; while (curr <= total) { usleep (50000 ); int rate = curr * 100 / total; cb (rate); curr += 10 ; } printf ("\n" ); } void processbar (int rate) { if (rate < 0 || rate > TOP) { return ; } int len = strlen (label); printf (GREEN "[%-100s]" NONE "[%d%%][%c]\r" , bar, rate, label[rate % len]); fflush (stdout); bar[rate++] = BODY; if (rate < TOP) { bar[rate] = RIGHT; } } #include "processBar.h" int main () { printf ("Download 1:\n" ); download (processbar); initbar (); printf ("Download 2:\n" ); download (processbar); initbar (); printf ("Download 3:\n" ); download (processbar); return 0 ; }
通过本文的实现,我们逐步完成了从基础进度条到支持多任务和彩色进度条的功能扩展。未来,可以进一步优化代码结构,支持更多功能,例如动态调整进度条长度、支持多种颜色等。