Quantcast
Channel: Planeta Código
Viewing all articles
Browse latest Browse all 2701

Blog Bitix: Escribir en la misma línea de la consola y obtener el ancho y alto de la terminal con Java

$
0
0
Java

Algunas aplicaciones en su salida en la terminal muestran una barra de progreso para la cual necesitan utilizar la secuencia de escape de la terminal o el caracter de carro para posicionar el cursor al inicio de la línea. En algunos casos incluso se muestran varias barras de progreso. Estos son los casos de los gestores de paquetes de GNU/Linux como pacman al realizar una actualización del sistema o de Gradle al descargar las dependencias.

Con las secuencias de escape se pueden cambiar los colores de los caracteres tanto del propio caracter como el color de fondo. Otras aplicaciones como el reproductor de música cmus muestran en la terminal una interfaz basada en texto con un barra de estado y varios paneles con la lista de las canciones del tamaño que tenga la terminal. Para esto es necesario conocer cuál es el tamaño de la terminal de columnas a lo ancho y de filas a lo alto.

Hay varias formas de conocer el tamaño de la terminal. Con el intérprete de comandos Bash el ancho y alto de la terminal se obtiene con las variables de entorno $COLUMNS y $LINES respectivamente. Pero también se puede obtener la misma información con el comando tput. Para obtener esta información desde un programa Java basta con ejecutar un proceso del sistema, obtener la salida de estos comandos y procesarla para obtener la información.

1
2
3
4
5
6
$ echo$COLUMNS$LINES8024
$ tput cols
80
$ tput lines
24

El siguiente ejemplo muestra varias barras de progreso utilizando la secuencia de escape \33[{COUNT}B, \33[{COUNT}A para posicionar el cursor una linea abajo o arriba y la información de ancho y alto de la terminal obtenida de ejecutar como un subproceso el comando tput.

 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
packageio.github.picodotdev.blogbitix.javaterminal;importjava.util.ArrayList;importjava.util.List;publicclassMain{publicstaticvoidmain(String[]args)throwsException{Terminalterminal=newTerminal();Printerprinter=newPrinter(terminal);List<Progress>progresses=newArrayList<>();List<Thread>threads=newArrayList<>();for(inti=0;i<5;++i){Progressprogress=newProgress(printer,i,i*5+5);progresses.add(progress);}for(Progressprogress:progresses){Threadthread=newThread(progress);thread.start();threads.add(thread);}for(Threadthread:threads){thread.join();}System.out.println();}}
 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
packageio.github.picodotdev.blogbitix.javaterminal;importjava.io.BufferedReader;importjava.io.InputStreamReader;importjava.nio.charset.Charset;publicclassTerminal{privateintwidth;privateintheigth;publicintgetWidth(){returnwidth;}publicintgetHeigth(){returnheigth;}synchronizedvoidrefresh(){try{ProcesscolsProcess=newProcessBuilder("bash","-c","tput cols 2> /dev/tty").start();ProcesslinesProcess=newProcessBuilder("bash","-c","tput cols 2> /dev/tty").start();BufferedReadercolsReader=newBufferedReader(newInputStreamReader(colsProcess.getInputStream(),Charset.forName("utf-8")));BufferedReaderlinesReader=newBufferedReader(newInputStreamReader(linesProcess.getInputStream(),Charset.forName("utf-8")));Stringcols=colsReader.readLine();Stringlines=linesReader.readLine();width=Integer.parseInt(cols);heigth=Integer.parseInt(lines);}catch(Exceptione){e.printStackTrace();}}}
 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
packageio.github.picodotdev.blogbitix.javaterminal;publicclassPrinter{privateTerminalterminal;privateintline;publicPrinter(Terminalterminal){this.terminal=terminal;this.line=0;}publicTerminalgetTerminal(){returnterminal;}synchronizedvoidprint(Stringtext,intline){setLine(line);erase();System.out.print(text);}voidrefresh(){terminal.refresh();}privatevoidsetLine(intline){Stringcommand="";if(this.line<line){command="B";}elseif(this.line>line){command="A";}if(!command.equals("")){System.out.print(String.format("\033[%s%s",Math.abs(this.line-line),command));this.line=line;}}privatevoiderase(){System.out.print("0f\33[2K\r");}}
 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
packageio.github.picodotdev.blogbitix.javaterminal;importjava.time.LocalDateTime;importjava.time.ZoneOffset;publicclassProgressimplementsRunnable{privatePrinterprinter;privateintline;privateintseconds;publicProgress(Printerprinter,intline,intseconds){this.printer=printer;this.line=line;this.seconds=seconds;}publicvoidrun(){longduration=seconds*1000;longstart=System.currentTimeMillis();longnow=start;while(start+duration>now){printer.refresh();now=System.currentTimeMillis();longpercent=Math.min(100,Math.round(((double)(now-start)/duration)*100));intsize=printer.getTerminal().getWidth()-11-8-2;longcharacters=Math.round((double)(size*percent/100));char[]chars=newchar[size];for(inti=0;i<chars.length;++i){chars[i]=(i<characters)?'#':'-';}StringnameStatus="jdk-openjdk";StringprogressStatus=String.valueOf(chars);StringpercentStatus=String.valueOf(percent)+"%";Stringstatus=String.format("%-11s [%s] %s",nameStatus,progressStatus,percentStatus);printer.print(status,line);sleep();}}privatevoidsleep(){try{Thread.sleep(500);}catch(Exceptione){e.printStackTrace();}}}
<noscript><a href="https://asciinema.org/a/237621" target="_blank"><img src="https://asciinema.org/a/237621.png" width="734" /></a></noscript> Progreso escribiendo en la misma línea de la consola

El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando el comando ./gradlew build && ./run.sh.


Viewing all articles
Browse latest Browse all 2701