Skip to content

Instantly share code, notes, and snippets.

@huseyin
Created March 25, 2016 15:58
Show Gist options
  • Save huseyin/a5226b32c3eeb545fc57 to your computer and use it in GitHub Desktop.
Save huseyin/a5226b32c3eeb545fc57 to your computer and use it in GitHub Desktop.
Simple progress bar experiment with C
/*
* Copyright (c) 2016 Hüseyin Tekinaslan <[email protected]>
*
* General license of the MIT.
* 17 December 2015, Mersin/TURKEY.
*/
#include <stdio.h>
#include <assert.h>
#include "progressbar.h"
// Ön tanımlı çubuk genişliği 80 karakterlik olsun. İlkel monitörleri de gözetelim.
enum { DEFAULT_WIDTH = 80 };
// Ön tanımlı adım saysını atadık.
enum { DEFAULT_STEP = 1 };
// Sayacın çıkacağı maksimum adım sayısını atadık.
enum { DEFAULT_TOTAL = 100 };
// Zaman parametrelerini göründüğü üzre saat/dakika/saniye olarak böldük. Bunların
// hesaplama algoritmaları aşağıda hesaplanmıştır.
struct time_literals {
int seconds;
int minutes;
int hours;
};
typedef struct time_literals time_components;
// Ön tanımlı ETA boş/dolu etiketlerinin ilk değerleri.
static const char *const FULL_ETA = "ETA: %2d:%02d:%02d";
static const char *const EMPTY_ETA = "--:--:--";
static void eta_format(progress *);
static time_components calculate_times(int);
// @tick ·> Sayacı bir tıklat, int argümanı kadar
static void progress_tick(progress *, int);
// @percent ·> Yüzde (%) gösterimi
static float progress_percent(progress *);
// @show ·> Progress çubuğunun son durumunu gör
static void progress_show(progress *);
// @position ·> Son durumu al
static int progress_position(progress *);
/*
* Progress çubuğunu bir ilkledirelim. Bunun için ayrı bir veri yapısı
* tutuldu. Aldığı argümanlar total adım, adım uzunluğu, çubuk genişliği
* ve adım karakteridir. Burada şöyle bir açıklama yapalım:
*
* -> int tipli argümanlara 1 verildiği anda öntanımlı değerlere geçer,
* -> char tipli değişkene NULL değeri verildiğinde öntanımlı çubuk
* karakterine geçer
*/
progress *ProgressBar(int total, int step, int width, char *complete_sep) {
progress *bar = (progress*)malloc(sizeof(progress));
/* İlk çalışma zamanını bi alalım. */
bar->time_now = time(NULL);
bar->total = (total == 1) ? DEFAULT_TOTAL : total;
bar->step = (step == 1) ? DEFAULT_STEP : step;
bar->width = (width == 1) ? DEFAULT_WIDTH : width;
bar->complete_sep = (complete_sep == NULL) ? "=" : complete_sep;
bar->current = 0;
return bar;
}
/* Sayaca bir dokunuş yap, adımlat. Sayaç ilerlesin. */
void progress_inc(progress *bar) {
progress_tick(bar, bar->step);
}
/* Progress çubuğuna anlık değerler set et. */
void progress_set(progress *bar, int n) {
if (n < 0)
assert(n < 0 && "argüman küçük");
if (bar->total < n)
assert(bar->total < n && "bar genişliği totalinden çok daha büyük");
if ((bar->total >= n) && (n >= 0))
bar->current = n;
progress_show(bar);
}
/*
* Progress çubuğunu adımlarını bitir. Anlık değeri, total
* değer eşitle.
*/
void progress_done(progress *bar) {
bar->current = bar->total;
progress_show(bar); // Ekrana çubuğun son halini bi yaz.
}
/* Çubuk adımlarını tamamladı mı? */
bool progress_isdone(progress *bar) {
return bar->current >= bar->total;
}
/* Çubuğun işgal ettiği bellek bölgelerini temizle. */
void progress_destroy(progress *bar) {
free(bar);
}
/* Çubuğun anlık adım durumunu artır. */
static void progress_tick(progress *bar, int n) {
bar->current += n;
/* Anlık değer 0'dan küçükse, 0 yap, */
if (bar->current < 0)
bar->current = 0;
/* Total değerden büyükse, total yap. */
if (bar->current > bar->total)
bar->current = bar->total;
progress_show(bar);
}
/* Progress çubuğunun anlık pozisyonunu al. */
static int progress_position(progress *bar) {
int pos;
pos = (int)((bar->current * (float)bar->width) / bar->total);
return pos;
}
/* İlerleme durumunun yüzdelik dilimi al. */
static float progress_percent(progress *bar) {
float percent;
percent = (float)bar->current / ((float)bar->total / (float)100);
return percent;
}
/* Progress çubuğunu ETA etiketleriyle birlikte ekrana yaz. */
static void progress_show(progress *bar) {
int i;
char line[1 << 10] = ""; // geçici bir çözüm. dinamik hale getirilmeli! (1 << 10 = 1024)
for (i=0; i < progress_position(bar); ++i) {
/*
* Ekrana durum çubuğunu yazarken şu sıraya dikkat edelim:
*
* ETA ETIKETI [==...] YUZDELIK DILIM
*
* şeklindedir.
*/
printf(" [%s%*s] %.2f %%\r", line, (bar->width-progress_position(bar)), "",
progress_percent(bar));
eta_format(bar);
strcat(line, bar->complete_sep);
fflush(stdout);
}
if (progress_isdone(bar))
printf("\n");
};
/*
* Zaman değişkenlerini hesaplamasını burada yapalım. Bir önceki
* yapı dikkate alınarak değişkenler (üretilen değerler) set edilsin.
*/
static time_components calculate_times(int time) {
int seconds = time % 60;
int minutes = (time / 60) % 60;
int hours = time / 3600;
time_components components = {seconds, minutes, hours};
return components;
}
/*
* ETA etiketini oluştur. İlk başlangıç zamanı ve çalışma zamanını
* arasındaki fark baz noktasıdır.
*/
static void eta_format(progress *bar) {
int elapsed;
int eta;
time_t new_time = time(NULL);
time_components tcomp;
if (bar->current == 0)
printf(EMPTY_ETA);
else {
elapsed = difftime(new_time, bar->time_now);
eta = elapsed * bar->total / bar->current - elapsed;
tcomp = calculate_times(eta);
printf(FULL_ETA, tcomp.hours, tcomp.minutes, tcomp.seconds);
}
}
#ifndef PROGRESS_BAR_H
#define PROGRESS_BAR_H
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <time.h>
typedef struct {
time_t time_now;
int total; // Progress total değeri
int step; // Progress adım değeri
int width; // ProgressBar genişliği
int current; // Progress'in anlık değeri
char *complete_sep; // ProgressBar prefix
} progress;
// Yeni bir Progress çubuğu oluşturma adımlarında şu parametreleri:
//
// @total Progress çubuğunun süreçteki alacağı maksimum değer
// @step Progress çubuğunda süreç adımlarını göster
// @width Progress çubuğunun eni-genişliği [==...]
// @current Sürecin anlık değeri
// @complete_sep Progress çubuğunun sayaç prefix'i
//
// @complete_sep ·> Ön tanımlı olarak '=' ekler. Ön tanımlı değeri
// atamak için NULL parametresini gönder.
progress *ProgressBar(int, int, int, char*);
// @increment ·> Sayacın artımını tıklat (inc)
void progress_inc(progress *);
// @set ·> Sayacın anlık durumunu dışardan tıklat (set et)
void progress_set(progress *, int);
// @done ·> Sayacı sıfırla
void progress_done(progress *);
// @@isdone ·> Sayaç sıfırda mı?
bool progress_isdone(progress *);
// @destroy ·> Aldığımızı geri veriyoruz
void progress_destroy(progress *);
#endif // PROGRESS_BAR_H
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment