├── cl-tqdm.asd ├── test.lisp ├── LICENCE ├── README.md └── cl-tqdm.lisp /cl-tqdm.asd: -------------------------------------------------------------------------------- 1 | (in-package #:cl-user) 2 | 3 | (asdf:defsystem :cl-tqdm 4 | :name "cl-tqdm" 5 | :description "Simple And Fast Progress Bar Library for Common Lisp" 6 | :author "hikettei" 7 | :version "v1.0" 8 | :license "MIT" 9 | :source-control (:git "git@github.com:hikettei/cl-tqdm.git") 10 | :serial t 11 | :components ((:file "cl-tqdm"))) 12 | 13 | -------------------------------------------------------------------------------- /test.lisp: -------------------------------------------------------------------------------- 1 | 2 | (in-package :cl-user) 3 | 4 | (load "cl-tqdm.lisp") 5 | 6 | (use-package :cl-tqdm) 7 | 8 | (defpackage :cl-tqdm/test 9 | (:use :cl :cl-tqdm)) 10 | 11 | (fresh-line) 12 | 13 | (format t "Welcome to cl-tqdm!🔰~%cl-tqdm can be used like:~%Displaying Losses and progress during training.~%") 14 | 15 | (with-tqdm x 10000 "Loss:0.0" 16 | (dotimes (i 10000) 17 | (sleep 0.001) 18 | (update x :description (format nil "Loss:~a.011" (random 10))))) 19 | 20 | (fresh-line) 21 | 22 | (format t "~%~%🗒 In the near future, to line up several progress-bars will be implemented...~%") 23 | (format t "~%🗒 More and documentations will be added!~%") 24 | (format t "~%✅ Pull requests are welcome at original repository!~%") 25 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 hikettei 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cl-tqdm 2 | Simple and Fast Progress Bar Library for Common Lisp, inspired in tqdm. 3 | 4 | (Optimizing hasn't done yet.) 5 | 6 | ![demo](https://gyazo.com/5bbc43310df9281c4711446ac7bb23b3/raw) 7 | 8 | (Currently estimation of remaining time does not work well lol. Pull requests are welcome I got tired.) 9 | 10 | This is the reimplementation of my old library [cl-cram](https://github.com/hikettei/cl-cram) because its APIs aren't useful and there is room to optimize. 11 | 12 | ## Run Test 13 | 14 | ```lisp 15 | $ sbcl --script test.lisp 16 | ``` 17 | 18 | ## Install 19 | 20 | Coming soon... (I've not registered it in quicklisp yet...) 21 | 22 | ## Features 23 | 24 | The basic usage is that: initialize `tqdmbar` structure with `(with-tqdm)` macro and then, call `(update)` function to increate the bar. 25 | 26 | `tqdmbar` structure can be initialized with `(tqdm count)` constructor. 27 | 28 | ```lisp 29 | (with-tqdm x 100 "No info" 30 | ; print-object will display progress bar but don't incf it. 31 | (print x) 32 | ;0% | | 0/100 [0.0s<0.0s, 0.0s/it] 33 | (update x :incf 2 :description "Hello world!" :stream t) 34 | ;Hello world!: 2% | | 2/100 [0.0s<0.0s, 0.0s/it] 35 | ) 36 | ``` 37 | 38 | If you don't want to use animated progress-bar in specific environment, `(with-config)` macro is available to solve this problem. 39 | 40 | This will make it possible to use cl-tqdm in SLIME REPL! ٩(๑>∀<๑)۶ 41 | 42 | ```lisp 43 | (with-config (config :animation nil) 44 | (with-tqdm x 10 "" 45 | (dotimes (i 10) 46 | (update x)))) 47 | ; 10% |█ | 1/10 [0.0s<0.0s, 0.0s/it] 48 | ; 20% |██ | 2/10 [0.0s<0.0s, 0.0s/it] 49 | ; 30% |███ | 3/10 [0.0s<0.0s, 0.0s/it] 50 | ; 40% |████ | 4/10 [0.0s<0.0s, 0.0s/it] 51 | ; 50% |█████ | 5/10 [0.0s<0.0s, 0.0s/it] 52 | ; 60% |██████ | 6/10 [0.0s<0.0s, 0.0s/it] 53 | ; 70% |███████ | 7/10 [0.0s<0.0s, 0.0s/it] 54 | ; 80% |████████ | 8/10 [0.0s<0.0s, 0.0s/it] 55 | ; 90% |█████████ | 9/10 [0.0s<0.0s, 0.0s/it] 56 | ; 100% |██████████| 10/10 [0.0s<0.0s, 0.0s/it] 57 | ``` 58 | ## Documents 59 | 60 | Coming soon... 61 | 62 | But the whole code is very simple, so if you want to know the usage, it is the best way to read the source code `./cl-tqdm.lisp` 63 | 64 | # Pull Requests 65 | 66 | Pull Requests are welcome at original repository [Here](https://github.com/hikettei/cl-tqdm) 67 | 68 | # LICENCE 69 | 70 | This package is under MIT Licence. See `./LICENCE` 71 | 72 | # Workloads 73 | 74 | - Fix: Estimating of resting time. 75 | - Todo: Optimize the whole codes 76 | - Todo: Prepare Example Code and Documentations 77 | - To Support: Jupyter 78 | 79 | # Author 80 | 81 | hikettei (Twitter: [@ichndm](https://twitter.com/ichndm)) 82 | 83 | -------------------------------------------------------------------------------- /cl-tqdm.lisp: -------------------------------------------------------------------------------- 1 | 2 | (defpackage :cl-tqdm 3 | (:use :cl) 4 | (:export 5 | #:tqdm 6 | #:config 7 | #:with-config 8 | #:with-no-animation 9 | #:with-tqdm 10 | #:update)) 11 | 12 | (in-package :cl-tqdm) 13 | 14 | (defstruct (TqdmBar 15 | (:conc-name tqdm-) 16 | (:print-function 17 | (lambda (tqdm stream depth) 18 | (declare (ignore depth)) 19 | (update tqdm :incf 0 :stream stream) 20 | nil)) 21 | (:constructor 22 | tqdm (total-count 23 | &optional 24 | (identifier :no-name) 25 | (description "") 26 | &aux (creation-time (get-universal-time))))) 27 | "Tqdm Structure that contains informations. 28 | 29 | APIs read this structure and update, rendering progress-bar in terminals. 30 | 31 | Example: 32 | 33 | ```lisp 34 | (print (tqdm :FirstBar 100)) 35 | ```" 36 | (identifier :no-name :type symbol) 37 | (total-count 0 :type fixnum) 38 | (count-idx 0 :type fixnum) 39 | (call-timestamps nil :type list) 40 | (creation-time 0 :type (integer 0 4611686018427387903)) 41 | (description "" :type string)) 42 | 43 | (defstruct (TqdmConfig 44 | (:conc-name config-) 45 | (:print-function 46 | (lambda (config stream depth) 47 | (declare (ignore depth)) 48 | (format stream "TqdmConfig{~% :animation ~a ~% :space-string ~a~% :bar-string ~a :indent ~a~%}" 49 | (config-animation config) 50 | (config-space-string config) 51 | (config-bar-string config) 52 | (config-indent config)))) 53 | (:constructor 54 | config (&key 55 | (animation t) 56 | (space-string " ") 57 | (bar-string "█") 58 | (indent 0)))) 59 | (animation t :type boolean) 60 | (space-string " " :type string) 61 | (bar-string "█" :type string) 62 | (indent 0 :type fixnum)) 63 | 64 | (defparameter *tqdm-config* (config 65 | :animation t 66 | :space-string " ")) 67 | 68 | (defparameter *in-update-method* nil) 69 | 70 | (defmacro with-tqdm (out 71 | total-size 72 | description 73 | &body 74 | body) 75 | "Example: 76 | (with-tqdm x :ProgressBar1 100 \"\" 77 | (update x))" 78 | (declare (type (or (unsigned-byte 64) symbol) total-size) 79 | (type string description)) 80 | `(let ((,out (tqdm ,total-size :with-tqdm ,description))) 81 | (fresh-line) 82 | ,@body)) 83 | 84 | (defmacro with-config (config &body body) 85 | "Example: 86 | (with-config (config :animation nil) 87 | (with-tqdm 88 | ~~~))" 89 | ; (declare (type TqdmConfig config)) 90 | `(let ((*tqdm-config* ,config)) 91 | ,@body)) 92 | 93 | (defmacro with-no-animation (&body body) 94 | `(with-config (config :animation nil) 95 | ,@body)) 96 | 97 | (defun update (tqdm &key (incf 1) (description "") (stream t)) 98 | (declare ;(optimize (speed 3)) 99 | (type symbol identifier) 100 | (type fixnum incf) 101 | (type tqdmbar tqdm)) 102 | (incf (tqdm-count-idx tqdm) incf) 103 | (setf (tqdm-description tqdm) description) 104 | (push (- (the 105 | (integer 0 4611686018427387903) 106 | (get-universal-time)) 107 | (tqdm-creation-time tqdm) 108 | (or 109 | (the (or null (integer 0 4611686018427387903)) (car (tqdm-call-timestamps tqdm))) 110 | 0)) 111 | (tqdm-call-timestamps tqdm)) 112 | (let ((*in-update-method* t)) 113 | (render-progress-bar stream tqdm) 114 | nil)) 115 | 116 | (defun progress-percent (status) 117 | (fround (* 100 (/ (tqdm-count-idx status) (tqdm-total-count status))))) 118 | 119 | (declaim (ftype (function (tqdmbar) string) render)) 120 | (defun render (status) 121 | "Rendering given status (which is the structure of tqdmbar), render returns the output string." 122 | (declare ;(optimize (speed 3)) 123 | (type tqdmbar status)) 124 | (with-output-to-string (bar) 125 | (let ((spl (- (config-indent *tqdm-config*) (length (tqdm-description status)) -1))) 126 | (write-string (tqdm-description status) bar) 127 | (dotimes (_ spl) (write-string " " bar)) 128 | (unless (equal (tqdm-description status) "") 129 | (write-string ":" bar) 130 | (write-string " " bar))) 131 | (let* ((n (the fixnum (round (the single-float (progress-percent status))))) 132 | (r (round (if (>= (/ n 10) 10.0) 10 (/ n 10))))) 133 | (if (< n 100) 134 | (write-string " " bar)) 135 | (write-string (write-to-string n) bar) 136 | (write-string "% |" bar) 137 | (dotimes (_ r) (write-string (config-bar-string *tqdm-config*) bar)) 138 | (dotimes (_ (- 10 r)) (write-string (config-space-string *tqdm-config*) bar))) 139 | (write-string "| " bar) 140 | (write-string (write-to-string (tqdm-count-idx status)) bar) 141 | (write-string "/" bar) 142 | (write-string (write-to-string (tqdm-total-count status)) bar) 143 | (write-string " [" bar) 144 | (let* ((now-time (get-universal-time)) 145 | (dif (- now-time (tqdm-creation-time status)))) 146 | (write-string (write-to-string (coerce dif 'single-float)) bar) 147 | (write-string "s<" bar)) 148 | (let* ((average-sec (coerce 149 | ; (/ (apply #'+ (tqdm-call-timestamps status)) 150 | ; (length (tqdm-call-timestamps status))) 151 | (/ (+ (or 152 | (car (tqdm-call-timestamps status)) 153 | 0.0) 154 | (or 155 | (second (tqdm-call-timestamps status)) 156 | 0.0)) 157 | (if (second (tqdm-call-timestamps status)) 158 | 2.0 159 | 1.0)) 160 | 'single-float)) 161 | (total (tqdm-total-count status)) 162 | (ts (tqdm-call-timestamps status)) 163 | (average-sec (/ average-sec (if (= 0.0 (car ts)) 164 | 1.0 165 | (car ts))))) 166 | (setf (tqdm-call-timestamps status) `(,0 167 | ,average-sec)) ;decay rate? 168 | (write-string (write-to-string (* average-sec total)) bar) 169 | (write-string "s, " bar) 170 | (write-string (write-to-string average-sec) bar) 171 | (write-string "s/it]" bar)))) 172 | 173 | (defun backward-lines () 174 | (write-char #\Return) 175 | (write-char #\Rubout)) 176 | 177 | (defun render-progress-bar (stream tqdm) 178 | (declare ;(optimize (speed 3)) 179 | (type TqdmBar tqdm)) 180 | 181 | (if (and 182 | (config-animation *tqdm-config*) 183 | *in-update-method*) 184 | ; delete the current progress-bar 185 | (backward-lines) 186 | (fresh-line)) 187 | (format stream (render tqdm)) 188 | nil) 189 | --------------------------------------------------------------------------------