Progress bar

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Post Reply
Message
Author
miskox
Posts: 631
Joined: 28 Jun 2010 03:46

Progress bar

#1 Post by miskox » 15 Jul 2024 12:50

This my very old post (viewtopic.php?p=33575#p33575) should be updated with escape sequences. So here are two versions:

First solution uses escape sequences just for cursor movement, other graphics is ASCII:

Code: Select all

@echo off
cls
echo                            P R O G R E S S     B A R
call :createbinvalue  27 DEL
call :createbinvalue 218 DEL
call :createbinvalue 196 DEL
call :createbinvalue 191 DEL
call :createbinvalue 179 DEL
call :createbinvalue 192 DEL
call :createbinvalue 217 DEL
call :createbinvalue 219 DEL
echo.
echo.
echo.
echo.
echo.
echo.

set esc=%char27%
REM hide cursor
echo %esc%[?25l

set spaces=&rem
set filler=&rem
set lines=&rem
for /L %%f in (1,1,80) do call set spaces= %%spaces%%
for /L %%a in (1,1,78) do call set filler=%%char219%%%%filler%%
for /L %%f in (1,1,78) do call set lines=%%char196%%%%lines%%

set "line0=0 %%                                 50 %%                                 100 %%"
set line1=%char218%%lines%%char191%<nul
set line3=%char192%%lines%%char217%<nul

set /a cnt=0
set /a cnt_all=200

:0
set /a percent=100 * cnt / cnt_all
call :drawprogressbar %percent% "downloading %cnt%"
set /a cnt+=1

if not "%cnt%"=="201" goto :0

REM show cursor
echo %esc%[?25h

goto :EOF

:drawprogressbar
REM par1=percent
REM par2=text to echo

set line4=%~2%spaces%
set line4=%line4:~0,78%
title %cnt%
set /a proc=(78 * %1 + 22 ) / 100

call set str1=%%filler:~0,%proc%%%
set str1=%str1%%spaces%
set str1=%str1:~0,78%

set line2=%char179%%str1%%char179%<nul

echo %esc%[9F%esc%[1G
echo.
echo %line0%
echo %line1%
echo %line2%
echo %line3%
echo.
echo %line4%

goto :EOF

:createbinvalue
REM par1=decima value between 0 and 255
REM par2: if DEL set the variable only (delete chr file)
md _chrs_>nul 2>nul
if exist _chrs_\%1.chr goto :gonext
set "options=/d compress=off /d reserveperdatablocksize=26"
if %~1 neq 26  (type nul >_chrs_\t.tmp
makecab %options% /d reserveperfoldersize=%~1 _chrs_\t.tmp _chrs_\%~1.chr >nul
type _chrs_\%~1.chr | (
(for /l %%N in (1 1 38) do pause)>nul&findstr "^">_chrs_\t.tmp)
>nul copy /y _chrs_\t.tmp /a _chrs_\%~1.chr /b
del _chrs_\t.tmp
) else (copy /y nul + nul /a _chrs_\26.chr /a >nul)
:gonext
if "%2"=="DEL" (
 for /F "delims=" %%y in (_chrs_\%1.chr) do set "char%1=%%y"
del _chrs_\%1.chr
rd _chrs_
)
goto :EOF
This one does not use ASCII graphics - it uses escape sequences.

Code: Select all

@echo off
cls
echo                            P R O G R E S S     B A R
call :createbinvalue  27 DEL
call :createbinvalue 219 DEL
echo.
echo.
echo.
echo.
echo.
echo.

set esc=%char27%
REM hide cursor
echo %esc%[?25l

set spaces=&rem
set filler=&rem
set lines=&rem
for /L %%f in (1,1,80) do call set spaces= %%spaces%%
for /L %%a in (1,1,78) do call set filler=%%char219%%%%filler%%
for /L %%f in (1,1,78) do call set lines=q%%lines%%

set "line0=0 %%                                 50 %%                                 100 %%"
set line1=l%lines%k<nul
set line3=m%lines%j<nul

set /a cnt=0
set /a cnt_all=200

:0
set /a percent=100 * cnt / cnt_all
call :drawprogressbar %percent% "downloading %cnt%"
set /a cnt+=1

if not "%cnt%"=="201" goto :0

REM show cursor
echo %esc%[?25h

goto :EOF

:drawprogressbar
REM par1=percent
REM par2=text to echo

set line4=%~2%spaces%
set line4=%line4:~0,78%
title %cnt%
set /a proc=(78 * %1 + 22 ) / 100

call set str1=%%filler:~0,%proc%%%
set str1=%str1%%spaces%
set str1=%str1:~0,78%

set line2=x%str1%x<nul

echo %esc%[9F%esc%[1G
echo.
echo %esc%(0%line0%
echo %line1%
echo %line2%
echo %line3%%esc%(B
echo.
echo %line4%

goto :EOF

:createbinvalue
REM par1=decima value between 0 and 255
REM par2: if DEL set the variable only (delete chr file)
md _chrs_>nul 2>nul
if exist _chrs_\%1.chr goto :gonext
set "options=/d compress=off /d reserveperdatablocksize=26"
if %~1 neq 26  (type nul >_chrs_\t.tmp
makecab %options% /d reserveperfoldersize=%~1 _chrs_\t.tmp _chrs_\%~1.chr >nul
type _chrs_\%~1.chr | (
(for /l %%N in (1 1 38) do pause)>nul&findstr "^">_chrs_\t.tmp)
>nul copy /y _chrs_\t.tmp /a _chrs_\%~1.chr /b
del _chrs_\t.tmp
) else (copy /y nul + nul /a _chrs_\26.chr /a >nul)
:gonext
if "%2"=="DEL" (
 for /F "delims=" %%y in (_chrs_\%1.chr) do set "char%1=%%y"
del _chrs_\%1.chr
rd _chrs_
)
goto :EOF
Image

Of course color could be added...


Saso

miskox
Posts: 631
Joined: 28 Jun 2010 03:46

Re: Progress bar

#2 Post by miskox » 15 Jul 2024 23:40

Update: 3rd version: instead of character 219 this version uses DEC character 'a' (▒). See https://vt100.net/docs/vt220-rm/chapter2.html#T2-4

Code: Select all

@echo off
cls
echo                            P R O G R E S S     B A R
for /f %%a in ('echo prompt $E^| cmd') do set "ESC=%%a"
echo.
echo.
echo.
echo.
echo.
echo.

REM hide cursor
echo %esc%[?25l

set spaces=&rem
set filler=&rem
set lines=&rem
for /L %%f in (1,1,80) do call set spaces= %%spaces%%
for /L %%a in (1,1,78) do call set filler=a%%filler%%
for /L %%f in (1,1,78) do call set lines=q%%lines%%

set "line0=0 %%                                 50 %%                                 100 %%"
set line1=l%lines%k<nul
set line3=m%lines%j<nul

set /a cnt=0
set /a cnt_all=200

:0
set /a percent=100 * cnt / cnt_all
call :drawprogressbar %percent% "downloading %cnt%"
set /a cnt+=1

if not "%cnt%"=="201" goto :0

REM show cursor
echo %esc%[?25h

goto :EOF

:drawprogressbar
REM par1=percent
REM par2=text to echo

set line4=%~2%spaces%
set line4=%line4:~0,78%
title %cnt%
set /a proc=(78 * %1 + 22 ) / 100

call set str1=%%filler:~0,%proc%%%
set str1=%str1%%spaces%
set str1=%str1:~0,78%

set line2=x%str1%x<nul

echo %esc%[9F%esc%[1G
echo.
echo %esc%(0%line0%
echo %line1%
echo %line2%
echo %line3%%esc%(B
echo.
echo %line4%

goto :EOF

Also reverse/normal could be used (display a space (blank)) with %esc%[7m %esc%[27m:

Code: Select all

@echo off
cls
echo                            P R O G R E S S     B A R
for /f %%a in ('echo prompt $E^| cmd') do set "ESC=%%a"
echo.
echo.
echo.
echo.
echo.
echo.

REM hide cursor
echo %esc%[?25l

set spaces=&rem
set filler=&rem
set lines=&rem
for /L %%f in (1,1,80) do call set spaces= %%spaces%%
for /L %%a in (1,1,78) do call set filler= %%filler%%
for /L %%f in (1,1,78) do call set lines=q%%lines%%

set "line0=0 %%                                 50 %%                                 100 %%"
set line1=l%lines%k<nul
set line3=m%lines%j<nul

set /a cnt=0
set /a cnt_all=200

:0
set /a percent=100 * cnt / cnt_all
call :drawprogressbar %percent% "downloading %cnt%"
set /a cnt+=1

if not "%cnt%"=="201" goto :0

REM show cursor
echo %esc%[?25h

goto :EOF

:drawprogressbar
REM par1=percent
REM par2=text to echo

set line4=%~2%spaces%
set line4=%line4:~0,78%
title %cnt%
set /a proc=(78 * %1 + 22 ) / 100

call set str1=%%filler:~0,%proc%%%
set str1=%esc%[7m%str1%%esc%[27m%spaces%
set str1=%str1:~0,87%

set line2=x%str1%x<nul

echo %esc%[9F%esc%[1G
echo.
echo %esc%(0%line0%
echo %line1%
echo %line2%
echo %line3%%esc%(B
echo.
echo %line4%

goto :EOF

Saso

miskox
Posts: 631
Joined: 28 Jun 2010 03:46

Re: Progress bar

#3 Post by miskox » 16 Jul 2024 00:52

Of course call to the :drawprogressbar should only be called 100 times and not each time a counter increases.

Code: Select all

@echo off
cls
echo                            P R O G R E S S     B A R
for /f %%a in ('echo prompt $E^| cmd') do set "ESC=%%a"
echo.
echo.
echo.
echo.
echo.
echo.

REM hide cursor
echo %esc%[?25l

set spaces=&rem
set filler=&rem
set lines=&rem
for /L %%f in (1,1,80) do call set spaces= %%spaces%%
for /L %%a in (1,1,78) do call set filler=a%%filler%%
for /L %%f in (1,1,78) do call set lines=q%%lines%%

set "line0=0 %%                                 50 %%                                  100 %%"
set line1=l%lines%k<nul
set line3=m%lines%j<nul

set old_prc=-1
set /a cnt=0
set /a cnt_all=200

:0
set /a percent=100 * cnt / cnt_all
if not "%old_prc%"=="%percent%" (
	call :drawprogressbar %percent% "downloading %cnt%"
	set old_prc=%percent%
)
set /a cnt+=1

if not "%cnt%"=="201" goto :0

REM show cursor
echo %esc%[?25h

goto :EOF

:drawprogressbar
REM par1=percent
REM par2=text to echo

set line4=%~2%spaces%
set line4=%line4:~0,78%
title %cnt%
set /a proc=(78 * %1 + 22 ) / 100

call set str1=%%filler:~0,%proc%%%
set str1=%str1%%spaces%
set str1=%str1:~0,78%

set line2=x%str1%x<nul

echo %esc%[9F%esc%[1G
echo.
echo %esc%(0%line0%
echo %line1%
echo %line2%
echo %line3%%esc%(B
echo.
echo %line4%

goto :EOF

Saso

T3RRY
Posts: 252
Joined: 06 May 2020 10:14

Re: Progress bar

#4 Post by T3RRY » 16 Jul 2024 03:25

Nice.

Since your using escape sequences, there is another Sequence thats handy for filling space like this - ESC[nX ; where n is the number of spaces to be filled, IE the length of the prograss bar

example (the macros are just a convenient way to animate it):

Code: Select all

@Echo off & Setlocal EnableDelayedExpansion
Mode 100,40 & CLS

Call:DefMacros

for /F %%a in ('Echo(prompt $E^| cmd')Do Set \E=%%a
Set /a END=151

%While:i.start=0% (
  %@frame:Args= CSdelay 1 % (
    Set /A "i+=1" , "iD=(i*100)/End"
    Set "Info=!i!/!end! {!iD!%%} !time!"
    %$StrLen:var=Info%
    Set /a "info.pos=100 - $len.info" , "R=255-((iD*20)/10)" , "G=((iD*15)/10)"
    Echo(%\E%[H%\E%[K%\E%[48;2;!R!;!G!;m%\E%[!iD!X%\E%[0m%\E%[E%\E%[!info.pos!G%\E%[0m!info!
    %While.GEQ:i.stop=!End!%
  )
)
Pause

Endlocal & goto:Eof

:DefMacros
(Set \n=^^^

%= do not modify this \n defintion =%)


Rem .|| usage: %@Frame:Args= ElapseVAR Centisconds %
Rem .:: elapseVar    = the variable name to be used for a specific frame. allows multiple independant framerates
Rem .:: Centiseconds = an Integer corresponding to the number of centisconds between frames. Supports the use of a variable name.
for /f %%! in ("! ! ^^^!")Do ^
Set @Frame=For %%. in (1 2)Do if %%.==1 (%\n%
  For /f "tokens=1,2 Delims= " %%G in ("Args")Do If not "%%!%%G.CS%%!"=="%%!time:~-1%%!" (%\n%
    Set /A "tDiff=%%!time:~-1%%!-%%G.CS","%%G.CS=%%!time:~-1%%!"%\n%
    If "%%!tDiff:~0,-1%%!"=="-" (%\n%
      Set /A "tdiff+=10,%%G=%%G %% 100000000 + tDiff"%\n%
    )Else Set /A "%%G=%%G %% 100000000 + tDiff"%\n%
    Set /a "Target=%%H","Frame=%%!%%G%%!"%\n%
    Set "FrameNAME=%%G"%\n%
  )%\n%
)Else if %%!FRAME%%! GEQ %%!TARGET%%! Set /A "Frame=0,%%!FrameName%%!=0"^& 

REM maximum number of iterations: 16*16*16*16*16 = 1,048,576
  Set "While=For /l %%i in (1 1 16)Do If Defined Do.While"
  Set "While=Set i.start=0&Set /A "Do.While=1,i=i.start"&!While! !While! !While! !While! !While! "
  Set "End.While=Set "i.Stop=1"& Set /A "While.Condition.End=i.Stop"&If ^!i^! CONDITION ^!While.CONDITION.End^! Set "Do.While=""
  For %%G in (LSS LEQ EQU GEQ GTR NEQ)Do Set "While.%%G=!End.While:CONDITION=%%G!"
  Set "End.While=Set "Do.While=""

REM .|| $StrLen implementation by Einst as inspired by https://www.dostips.com/forum/viewtopic.php?f=3&t=1429&sid=0786ad8d7d522f4ef0ba851f1bea301d
Set "$strlenHASH=var"
for /f %%! in ("! ! ^^^!")Do ^
set $StrLen=If /i not "var"=="%%!$StrlenHASH%%!" (%\n%
    set "$=A%%!Var%%!"%\n%
    set "$3=%%!$:~7167,1%%!%%!$:~6143,1%%!%%!$:~5119,1%%!%%!$:~4095,1%%!%%!$:~3071,1%%!%%!$:~2047,1%%!%%!$:~1023,1%%!FEDCBA9876543210"%\n%
    set /A "$len.Var=0","$len.Var+=$3=0x%%!$3:~15,1%%!*1024"%\n%
    for %%# in (%%!$3%%!) do set "$=%%!$:~%%#%%!"%\n%
    if defined $ (%\n%
      set "$2=%%!$:~959,1%%!%%!$:~895,1%%!%%!$:~831,1%%!%%!$:~767,1%%!%%!$:~703,1%%!%%!$:~639,1%%!%%!$:~575,1%%!%%!$:~511,1%%!%%!$:~447,1%%!%%!$:~383,1%%!%%!$:~319,1%%!%%!$:~255,1%%!%%!$:~191,1%%!%%!$:~127,1%%!%%!$:~63,1%%!FEDCBA9876543210"%\n%
      set /A "$len.Var+=$2=0x%%!$2:~15,1%%!*64"%\n%
      for %%# in (%%!$2%%!) do set "$=%%!$:~%%#%%!"%\n%
      set "$1=%%!$%%!FEDCBA9876543210FEDCBA9876543210FEDCBA9876543210FEDCBA98765432103333333333333333222222222222222211111111111111110000000000000000"%\n%
      set /A "$len.Var+=0x%%!$1:~127,1%%!%%!$1:~63,1%%!-1"%\n%
    ) else set /A "$len.Var-=1"%\n%
)
Goto:Eof

miskox
Posts: 631
Joined: 28 Jun 2010 03:46

Re: Progress bar

#5 Post by miskox » 16 Jul 2024 04:56

T3RRY wrote:
16 Jul 2024 03:25
ESC[nX
How I missed that? It has been quite a long time from the last time I used them (on DIGITAL's VT420 and VT520 video terminals on OpenVMS).

Thank you.
Saso

jeb
Expert
Posts: 1055
Joined: 30 Aug 2007 08:05
Location: Germany, Bochum

Re: Progress bar

#6 Post by jeb » 21 Oct 2024 02:34

And now my own try of a progress bar. But with the use of 8 different steps per single character.
It shows the progress bar in the window and in the title

I'm using the unicode characters from U+2588 to U+258F.









How it looks depends on the selected font.
My best results are with a unix machine, ssh'ing to the window system :D

Code: Select all

@echo off
setlocal
chcp 65001 >NUL

call :createUnicodeCharText special_chars "U+8, U+1B, U+7"
call :createUnicodeCharText progress_chars "U+258F, U+258E, U+258D, U+258C, U+258B, U+258A, U+2589, U+2588"

setlocal EnableDelayedExpansion

set "BS=!special_chars:~0,1!"
set "ESC=!special_chars:~1,1!"
set "BEL=!special_chars:~2,1!"

REM *** Hide Cursor
<nul set /p "=%ESC%[?25l"

echo %time%
set "total="
for /L %%o in (1 1 25) do (
    for /L %%# in (0 1 7) DO (
        <nul set /p "=!progress_chars:~%%#,1!!BS!%ESC%]0;Title!total!!progress_chars:~%%#,1!%BEL%"
        REM *** Wait 10ms
        set "old=!time!"
        if defined old for /L %%1 in ( 0 1 9 ) do if defined old for /L %%2 in ( 0 1 9 ) do if defined old for /L %%3 in ( 0 1 9 ) do if defined old for /L %%4 in ( 0 1 9 ) do (
            if defined old if "!old!" NEQ "!time!" (set "old=")
        )
    )
    <nul set /p "=!progress_chars:~7,1!"
    set "total=!total!!progress_chars:~7,1!"
)

echo(
echo %time%

REM *** Enable Cursor
<nul set /p "=%ESC%[?25h"
exit /b

set "old=%time%"
for /L %%# in ( 1 1 16 ) do for /L %%# in ( 1 1 16 ) do for /L %%# in ( 1 1 16 ) do (
    if "!old!" NEQ "!time!" exit /b
)
exit /b
:createUnicodeCharText
setlocal EnableDelayedExpansion
set "tmp=%~2"
(set \n=^
%==%
)
set "utf16_le_byte_list=ff fe"
FOR %%L in ("!\n!") do FOR /F "tokens=1 delims= " %%U in ("!tmp:,=%%~L!") DO (

    set "code_point=%%U"
    set "code_point=!code_point:U+=!"
    set /a code_point=0x!code_point!
    if !code_point! LSS 0x10000 (
        call :dec2hex_le !code_point! utf16_le_bytes
    ) ELSE (
        set /a "code_point-=0x10000"
        set /a "W1_val=((code_point >> 10) & 0x3FF) | 0xD800"
        set /a "W2_val=(code_point & 0x3FF) | 0xDC00"
        call :dec2hex_le !W1_val! W1_hex
        call :dec2hex_le !W2_val! W2_hex
        set "utf16_le_bytes=!W1_hex! !W2_hex!"
    )
    set "utf16_le_byte_list=!utf16_le_byte_list! !utf16_le_bytes!"
)

> "!TEMP!\utf16-LE.tmp" (echo !utf16_le_byte_list!)

rem *** Decode file to binary
> nul CertUtil -f -decodehex "!TEMP!\utf16-LE.tmp" "!TEMP!\utf16-LE.tmp"

for /F %%C in ('type "!TEMP!\utf16-LE.tmp"') do (
    del "!TEMP!\utf16-LE.tmp"
    endlocal
    set "%~1=%%C"
)
exit /b

:dec2hex_le
set "hex=0123456789ABCDEF"
set val=%1
set "res="
set /a "val|=0x10000"

FOR /L %%# in (0 1 3) DO (
    set /a "nibble=val & 0x0F"
    set /a "val>>=4"
    for /F %%N in ("!nibble!") DO set "res=!hex:~%%N,1!!res!"
)
set "%2=!res:~2,4! !res:~0,2!"
exit /b

miskox
Posts: 631
Joined: 28 Jun 2010 03:46

Re: Progress bar

#7 Post by miskox » 21 Oct 2024 05:18

Very good! Thanks.

Saso

Post Reply