Alisson Machado
24 December 2019

Python, NCurses, Thread: Criando uma interface de chat

Uma vez a época em que fazia um curso técnico de informática na etec, eu assisti um filme do Hulk. Nele tem uma cena onde o Mr. Green conversava com o Mr. Blue através de um chat via terminal na época eu fiz uma interface bem parecia utilizando a biblioteca ncurses do C.

A imagem acima é a tela criada com o script que eu vou ensinar aqui no tutorial. Como ultimamente tenho usado muito windows, pensei em como eu poderia criar uma interface assim também pra usar no PowerShell, óbvio no Linux a qualidade sempre fica melhor, mas é possível chegar perto disso e vou mostrar como. Primeiro passo é instalar a biblioteca:

pip install windows-curses

O código completo ficou assim, mais a frente vou explicar em partes.

import sys
from curses import wrapper
import curses
from threading import Thread
import time
from random import randint

def board(stdscr):
    stdscr.clear()
    curses.echo()
    lasty, lastx = stdscr.getmaxyx() 
    msg_box = curses.newwin(lasty-4,lastx,0,0)
    msg_box.addstr("== Message System == \n")
    i = 0
    msg_box.scrollok(True)
    while i <= 10:
        msg_box.refresh()
        msg_box.addstr("Mr. Blue>  Mensagem %s \n"%i)
        i = i+1
        time.sleep(randint(1,5))

def main(stdscr):
    curses.echo()
    lasty, lastx = stdscr.getmaxyx() 
    stdscr.addstr("=== sistema muito doido ===\n")
    input_box = curses.newwin(0,lastx,lasty-3,0)    
    while True:
        input_box.clear()
        input_box.refresh()
        input_box.addstr("Mr. Green > ")
        msg = input_box.getstr()
        if msg == b"#exit":
            return

t = Thread(target=wrapper,args=(board,))
t.start()
wrapper(main)

Agora entendendo o código. A biblioteca ncurses do Python possui um facilitador que chamamos de Wrapper.

from curses import wrapper

Ele é uma função que facilita um pouco a detecção do terminal que você está usando, ele recebe como argumento uma função na qual você pode definir as configurações da tela. No meu caso eu precisava de duas telas que fossem independentes, sendo uma telas uma Thread , que vai receber as mensagens do servidor e printar essas mensagens na tela. A outra tela é basicamente o espaço onde eu vou digitar as minhas mensagens.

def board(stdscr):
    stdscr.clear()
    curses.echo()
    lasty, lastx = stdscr.getmaxyx() 
    msg_box = curses.newwin(lasty-4,lastx,0,0)
    msg_box.addstr("== Message System == \n")
    i = 0
    msg_box.scrollok(True)
    while i <= 10:
        msg_box.refresh()
        msg_box.addstr("Mr. Blue>  Mensagem %s \n"%i)
        i = i+1
        time.sleep(randint(1,5))

No trecho de código acima, eu criei uma janela no terminal que vai ter a altura total - 4, pois dessa posição pra baixo, vai ser onde eu vou digitar as minhas mensagens. O método

getmaxyx()


foi utilizado para pegar o tamanho total do terminal. O método

msg_box.scrollok(True)

, foi utilizado para que o programa não de erro, então assim que as mensagens atingirem o limite da tela, a mais antiga será apagada e a mais recente aparecerá no final da tela. Dentro dessa função criei um looping que conta até 10 e dentro dele eu adiciono strings como se fosse o Mr. Blue conversando, essas mensagens sempre vão aparecer em um intervalo aleatório dentro do limite de 1 a 5 segundos.

def main(stdscr):
    curses.echo()
    lasty, lastx = stdscr.getmaxyx() 
    stdscr.addstr("=== sistema muito doido ===\n")
    input_box = curses.newwin(0,lastx,lasty-3,0)    
    while True:
        input_box.clear()
        input_box.refresh()
        input_box.addstr("Mr. Green > ")
        msg = input_box.getstr()
        if msg == b"#exit":
            return

Nessa função eu basicamente criei uma segunda tela, que começa da posição


lasty - 3


ou seja, 3 linhas acima do final da tela e deixei esse espaço reservado para digitar as mensagens. Dentro dessa janela foi colocado um

While True

, que fica aguardando por mensagens que o usuário digitar e caso seja digitado

#exit

o programa é finalizado. E fim =)