Skip to main content

3 posts tagged with "gui"

View All Tags

Thiago Lages de Alencar

Esse post é meio que uma compilação do que eu entendi de cada assunto após de horas lendo na internet e perguntando para meu pai.
Em outras palavras: Pode ter informação incorreta!

TTY

Teletype
(https://en.wikipedia.org/wiki/Teleprinter)

TTY é um comando em linux para saber o nome do terminal o qual a input está diretamente conectada.

$ tty
/dev/pts/1

$ echo "example" | tty
not a tty

No primeiro exemplo, o comando tty veio como input diretamente do terminal.

No segundo exemplo, o comando tty recebeu a input example do comando anterior (não de um terminal).

Fim! Pode ir para a próxima sessão, ao menos que você queira saber o que diabos é uma teletype.

Rosto curioso

Já notou que muitas coisas no computador possuem o nome de objetos que existem fora do computador?

Acontece que o nome é dado baseado nestes objetos para ajudar usuários a entenderem melhor o uso deles no computador! Por exemplo:

  • file
  • folder
  • trash can
  • window

O mesmo vale para TTY, onde o nome veio de teletypes. Infelizmente não é um nome que ajude muito pois computadores já subsittuiram o uso delas então esse nome não ajuda ninguém a saber do que o comando se trata 🤣.

O que são teletypes?
Entenda que elas são uma junção de typewriters e telegraph key.
O primeiro utilizado para escrever em papel e o segundo utilizado para enviar morse code a distância.

Morse code era muito utilizado como uma forma de comunicação binária via cabo (som curto/longo), então teletypes desenvolveram lógicas para converter esses sinais para letras e vice-versa. Por isto a chegada delas subistituiu tradutores de morse code, elas viraram como novo meio de enviar e receber mensagens.

Ao mesmo tempo computadores e terminais estavam nascendo. Alguns terminais vinham com capacidade de receber/escrever informação das/nas teletypes (afinal é tudo binário).

Uma coisa que não ficou claro para mim é se inicialmente teletypes eram usadas para enviar/receber mensagem dos computadores iniciais.
Bem, a essa altura pelo menos ficou claro que a funcionalidade a qual elas foram inspiradas era a capacidade de enviar/receber dados.

Este video mostra uma teletype recebendo e enviando dados de/a um terminal:
https://www.youtube.com/watch?v=S81GyMKH7zw

Por isto que o termo TTY era utilizado para referência aparelhos enviando/recebendo (input/output) mensagem do computador.

Algumas linguagens até incluem código para fazer essa verificação:

  • C
    • #include <unistd.h>

      isatty(fildes);
  • Python
    • import os

      os.isatty(fd)
  • NodeJS
    • tty.isatty(fd)

Onde a funcionalidade das funções é identificar se a input/ouput está vinculada a um aparelho (device).

warning

Preste bem atenção que sua input pode estar ligada ou não a um aparelho E sua output pode estar ligada ou não a um aparelho.
Um deles estar ligado não quer dizer que ambos estão.

As funções recebem um file descriptor e dizem se ele está ou não linkado a um aparelho.
Você poderia passar STDIN, STDOUT ou até STDERR para a função analisar.

Esse video cobre bem o assunto: https://www.youtube.com/watch?v=SYwbEcNrcjI

Se realmente quiser saber detalhes sobre TTY, existe este blog cheio de informações (que eu não li):
https://www.linusakesson.net/programming/tty/

TTY

Terminal

(https://en.wikipedia.org/wiki/Computer_terminal)

Teletypes originalmente eram conhecidas como "hard-copy terminals" por usarem papel, mas com a vinda de telas nós formamos uma nova ideia de terminal nas nossas cabeças (a tela preta).

Terminal não possui armazenamento de dados, da mesma maneira que teletypes apenas eram responsáveis por ler e escrever do computador, ou seja, a lógica ainda estava no computador. Alguns terminais possuiam um pouco de lógica neles porém nada comparado ao computador.

Se você viu o video da sessão anterior então já deve ter ganhado uma ideia do que é um terminal, pois nele é mostrado uma teletype lendo e escrevendo para um terminal.
Mas caso queira outro video mostrando melhor um terminal:
https://www.youtube.com/watch?v=UNdu0YQfvF0

Terminal

Terminal Emulator

(https://en.wikipedia.org/wiki/Terminal_emulator)

Hoje em dia usamos o termo terminal para representarmos emuladores de terminais.

Diferentemente de terminais, estes estão fortemente ligados a computador e não são máquinas separadas da lógica. Basicamente estamos falando da janela que finge ser um terminal (GNOME terminal).

Terminal Emulator

Shell

(https://en.wikipedia.org/wiki/Shell_script)

Um programa responsável por ficar em loop esperando comandos do usuário para serem executados.

Comandos podem ser:

  • Programas
    • echo
    • ls
    • touch
    • mkdir
    • Buscados em lugares pré definidos (/bin, /usr/bin, ...)
      • Use echo $PATH para ver a lista de lugares a se olhar
  • Comandos do próprio shell
    • type
    • which
    • help
    • man
    • Estes existem dentro do shell e não precisam ser buscados.
  • Shell functions
  • Aliases
    • Comandos definidos por nós, construido de outros comandos

Existem variações e alternativas de shell:

Shell

CLI

Command-line interface
(https://en.wikipedia.org/wiki/Command-line_interface)

É uma interface, ou seja, maneira do programa dar mais controle ao usuário sobre o programa.

Está interface se basea no usuário passar flags e mais informações em conjunto ao comando, dessa maneira mudando o comportamento do commando. Por exemplo, o programa ls disponibiliza diversas flags para alterar o comportamento:

  • ls
    • Lista tudo no diretório atual mas ignora os começando com .
  • ls -a
    • Lista tudo no diretório atual e não ignora os começando com .
  • ls -l
    • Lista tudo no diretório atual mas com mais detalhes

Fique bem claro que é o programa te dando opções de como interagir com ele, não o shell ou terminal, então resta ao programa implementar comportamentos para certas flags.

note

É muito comum programas oferecerem detalhes sobre as flags quando utilizando a flag --help (ls --help).

CLI

TUI

Terminal user interface
(https://en.wikipedia.org/wiki/Text-based_user_interface)

Novamente é uma interface, ou seja, maneira do programa dar mais controle ao usuário sobre o programa. Porém está foca em dar uma interação mais visual e continua.

Diferente de CLI's onde toda a interação começa e termina em um comando só, TUI's continuam esperando mais interações do usuário até um dos dois decidirem terminar.

Um exemplo bem comum é top que providência uma visão dos programas/processos/threads em execução do sistema, uma vez inicializado ele esperar por mais interações do usuário. Se você apertar q ele termina, se você apertar h ele fornece a lista de comandos, etc.

Note que a TUI's ainda podem providênciar flags para alterar o comportamento (top --help).

TUI

GUI

Graphical user interface
(https://en.wikipedia.org/wiki/Graphical_user_interface)

Novamente é uma interface, ou seja, maneira do programa dar mais controle ao usuário sobre o programa. Porém não está limitada a usar texto para a visualização, pois tem a capacidade de desenhar na tela.

Hoje em dia é o meio mais popular de se usar uma aplicação, quando se abre VSCode, Google Chrome, Discord... Todos são GUI's pois utilizaram a capacidade de desenhar para dar uma interface ao usuário.

Mesmo programas focados em GUI's podem aceitar flags (VSCode: code --help).

GUI

References

Thiago Lages de Alencar

No post anterior declarei que busquei por um tempo uma interface gráfica (GUI) para MongoDB porém não fiquei satisfeita com nenhuma.

Neste post vou falar do desenvolvimento do Mondot, interface gráfica para o banco MongoDB.

Por favor, levar em conta que eu posso tomar decisões ruins 🤣

Reason

Em 2021, eu tinha duas coisas em mente:

  • Nenhuma GUI de Mongo me agrada
  • Quero aprender Godot

Bastou essas duas coisas para eu querer começar este projeto.
Eu não queria fazer a melhor GUI do mundo, eu queria me divertir com Godot ao mesmo tempo que resolvia um incomodo que eu tinha com GUIs.

Start

Abri o Godot, comecei a criar containers, janelas, botões, etc. Dias depois me veio a pergunta:

Como diabos eu vou me comunicar com o Mongo?

Rosto de idiota com o olhar torto para fora

Eu vou pegar o texto que o usuário escrever e fazer o que com ele?
Passar para um Node.js?
Eu vou ter que instalar Node.js na máquina da pessoa?
Como eu pego a resposta?
Eu conseguiria usar GDNative?
Como que os outros projetos fazem isto?

Agora eu precisava descobrir como faria isto acontecer.

Cheating

Colando do colega do lado

Robomongo! Também conhecido como Robo 3T.
Um projeto open-source que em 2017 foi adquirido pelos criadores do Studio 3T.

O que importa é que eu tenho um projeto para copiar estudar!

Robo 3T possui dois repositórios:

  • robomongo
    • Responsável pela interface gráfica
  • robomongo-shell
    • Responsável pela interação com o Mongo.

Importante notar que o segundo é um fork do Mongo oficial, justamente pois nele é incluido um shell interativo para se comunicar com o banco.

Isso faz sentido, já que a interface de linha de comando geralmente é a primeira coisa a ser feita para interagir com bancos. Como mais as pessoas interagiriam com o banco antes de GUI existirem? Poderia ser por código mas seria um baita trabalho cada operação.

Então era isso, tudo que eu tinha que fazer era:

  1. Incluir o shell do Mongo na minha interface gráfica
  2. Utilizar o modo iterativo do shell

Problems

Eu não tinha ideia de como fazer para incluir o shell na interface gráfica.
Decidi aceitar que passaria como o shell separado para o usuário e dei essa parte como dada.

Eu que não vou reclamar das minhas decisões idiotas no meu projeto pessoal.

Shrug face

Agora tudo que eu tinha que fazer é chamar o shell pelo Godot.
Para isto eu iria precisar usar o método OS.execute().

int execute (
String path,
PoolStringArray arguments,
bool blocking=true,
Array output=[],
bool read_stderr=false,bool open_console=false
)

O problema é que essa função possui dois comportamentos dependendo se blocking for true ou false, porém nenhum dos dois era o que eu buscava.

Outro rosto de idiota com o olhar torto para fora

blocking -> true: Godot vai pausar enquanto a saída não é escrita em output.

Não queremos isto pois uma busca pode resultar em milhares de documentos. Queremos retornar mais documentos conforme o usuário pedir por mais documentos.

blocking -> false: Godot vai continuar executando e o comando irá rodar em um processo separado. Porém não será possível recuperar o output do comando.

Nós queremos receber a saída aos poucos se estivermos falando de pesquisas que resultam em muitos documentos.

Função do Godot não é interativa, como vou usar o shell interativo?
Posso ler input e output usando algum conhecimento do sistema operacional?
Eu vou ter que conhecer bem todos sistemas operacionais?
Vou ter que descobrir como Robo 3T linka o shell?

Solution

Criar meu próprio shell.

Shell

Vantagens:

  • Utilizar a linguagem que mais estou em dia Python
    • Em outras palavras: Nenhum tempo perdido por errar algo de uma linguagem que não estou acostumado (JavaScript)
  • Utilizar a mesma linguagem com qual trabalho para fazer queryies
  • Utilizar o padrão de comunicação que eu quiser
    • No próximo tópico você vai entender o que eu quero dizer com isto

Se estivessemos falando de um produto para diversos usuários, definitivamente JavaScript seria a melhor escolha.

Para projeto pessoal com interesse em Godot? Utilizar a linguagem que mais tenho conhecimento no momento.

Communication

Shell

De todas as maneiras para duas aplicações se comunicarem, eu escolhi a mais simples.

Arquivos... :D

Em vez de quebrar a cabeça para entender como eu poderia reproduzir a interatividade do shell do Mongo, eu poderia apenas fazer com que ambos escrevessem em arquivos quando quisessem se comunicar um com o outro.

O processo em si é bem simples:

  1. Godot executa o shell, passando a query
  2. Shell escreve X documentos em um arquivo
  3. Godot solicita mais documentos

Note que você deve repetir etapa 2 e 3 até acabar os documentos ou Godot mandar parar.

Start

Primeira parte da comunicação entre shell e Mondot

  1. Godot escreve o código Python em um arquivo
    • xxxxx representa o nome do arquivo
    • O nome do arquivo é aleatório
  2. Godot executa o shell passando o caminho para o arquivo como parâmetro

Output

Segunda parte da comunicação entre shell e Mondot

  • Godot fica periodicamente conferindo se o arquivo de saída existe
    • xxxxx_out_0 representa o primeiro arquivo de saída
      • xxxxx_out_1 segundo arquivo de saída
      • xxxxx_out_2 terceiro arquivo de saída
      • etc
  • Shell escreve no arquivo de saída um JSON com o resultado da query
    • O resultado de uma query pode ser partido em diversos arquivos
      • xxxxx_out_0, xxxxx_out_1, xxxxx_out_2, ...
      • Esses arquivos são criados conforme o usuário solicita mais documentos do resultado

Input

Terceira parte da comunicação entre shell e Mondot

  • Godot escreve no arquivo de entrada solicitando mais documentos do resultado da query
  • Shell fica periodicamente conferindo se tem algo escrito no arquivo de entrada
    • xxxxx_in representa o arquivo de entrada
    • Ele lê o arquivo e remove o conteúdo do arquivo
      • Justamente para receber futuras solicitações pelo mesmo arquivo

Shell

O que o meu shell precisa fazer?

  • Receber o código do usuário
  • Executar o código do usuário
  • Iterar sobre o resultado do código
    • Iterar em partes (pegando X documentos por vez)

Em outras palavras, eu preciso acoplar o código do usuário ao código do shell.

Código do usuário sendo acoplado no meio do código do shell

Note que existem operações que meu shell irá fazer antes e após a execução do código do usuário. Por causa disso, o desenho mostra o código no meio do código shell.

O plano era receber um código simples do usuário, parecido com o do shell do Mongo:

db.test.find({})

Attempt 1

eval(expression, globals=None, locals=None)

Bom: A função faz o parser e avalia a expressão, retornando o resultado dela.

result = eval("""
db.test.find({})
""")

Ruim: Precisa ser uma expressão. Uma ou mais declarações não funcionam.

# Error
result = eval("""
db.test.find({})
db.test.find({})
""")

Attempt 2

exec(object, globals=None, locals=None, /, *, closure=None)

Bom: Executa múltiplas declarações.

exec("""
db.test.find({})
db.test.find({})
""")

Ruim: Cada declaração pode ter ou não um resultado, então essa função não retornar nada.

# result is None
result = exec("""
db.test.find({})
db.test.find({})
""")

Problem

Nas tentativas acima, para obter o resultado desejado eu teria que alterar a string. Porém, isto apresenta grande risco da alteração dar erro pois cada caso pode requer uma alteração diferente.

No final eu seria forçado a ler o código, entende-lo para depois alterar sem grande chance de erros. Mas sabe quem já faz isso de ler e entender código? O compilador.

AST

Desenho de uma árvore para representar AST

A compilação de uma linguagem envolve diversas etapas. Uma delas envolve gerar uma árvore sintática abstrata, uma árvore que garante que os objetos estão ligados corretamente.

Podemos usar este conhecimento para compilar parcialmente o código do usuário. Por exemplo, o seguinte código:

for x in db.test.find({}):
db.test.update({"_id": x["_id"]}, {"code": x["code"] + 1})
db.test.find({})

Gera uma árvore com a seguinte raiz:

Simples AST

Note que eu só mostrei o início da árvore pois todo o resto é irrelevante para nós.
Não vamos alterar nada que esteja a fundo do código do usuário, apenas na raiz.

Com esse conhecimento em mão, podemos finalmente alterar o código do usuário!

info

Utilizei um módulo do próprio Python chamado ast para analisar e reestruturar a AST.

Rewriting code

Vamos criar um código inútil apenas para usar como exemplo:

# User code
doc = db.test.find_one({})
db.test.update_one({}, {"valid": True})
db.test.find({})

A idéia é transformar este código em uma função que o shell poderá chamar e receber de volta o valor da última expressão. Em outras palavras, queremos isto:

def code():
# User code
doc = db.test.find_one({})
db.test.update_one({}, {"valid": True})
return db.test.find({})

Vamos ao passo a passo de como obter isto:

  1. Análisar o código do usuário com AST
  2. Inserir o conteúdo do módulo dentro de uma função pré montada
  3. Encapsular a última expressão em um return
    • Apenas fazer isto se for uma expressão que retorna valor

O video seguinte demonstra a transformação que está sendo feita de certa forma.

Conclusion

Com isto eu consegui preparar justamente uma interface gráfica para o Mongo em Godot. Esse foi meu projeto de 2021!


Em 2023 eu decidi dar uma refatorada e criei uma segunda versão. Mas toda essa base vista do Mondot ainda é a mesma!


Agora já estou cansado de falar deste projeto hahahaha, foi bom enquanto durou.

References

Thiago Lages de Alencar

Em 2019 eu comecei a trabalhar em uma startup que usava banco de dados MongoDB.

Na época mantermos diversas licenças da interface gráfica Studio 3T não era uma boa ideia, não eramos uma empresa lucrativa e o uso dela por pessoa não iria valer a pena.

E por isso comecei a buscar por um interface gráfica que fosse me satisfazer.

Pessoa caminhando com uma madeira carregando seus pertences na ponta

Robot 3T

Uma alternativa considerada foi Robo 3T/Robomongo mas pelo fato dele ter sido adquirido pelo Studio 3T já não era um bom sinal para mim.

É muito normal ver empresas grandes comprando concorrentes e depois deixando eles aos poucos sumirem, então meu palpite era que Robot 3T não iria receber tantas atualizações e não cresceria como ferramenta.

Hoje em em 2023: Fazendo 6 anos desde que foi adquirido e uma versão de graça do Studio 3T foi lançada para substituir o Robo 3T que foi descontinuado.

Mongo-Express

Utilizei mongo-express por um tempo e satisfazia o mínimo que eu precisava mas a interface gráfica não me agradava.

Hoje em em 2023: Parece ter mantido a mesma interface, o que não me atrai.

QueryAssist

Em 2020, QueryAssist foi a ferramenta que eu decidi utilizar sempre para interagir com o Mongo.

Ela possuia uma versão gratuita mas você podia comprar a versão vitalícia e ganhar mais funcionalidades. A versão gratuita era mais que o suficiente para mim e funcionava muito bem.

Infelizmente durou pouco, fui notando a falta de atualização no blog e nos posts do Twitter. Claramente um sinal que a ferramenta iria desaparecer, por causa disso decidi retornar a minha busca por outra interface gráfica antes que está parece de funcionar sem aviso prévio.

Hoje em em 2023: Site deles foi desligado e eles não tem postagens novas desde 2020.

MongoDB Compass

MongoDB Compass parece estar em construção desde 2015, mas na época que eu estava buscando interfaces gráficas era como se ele não existisse para mim. Meu palpite era que não estava divulgando já que não estava pronto para produção 🤔.

Tudo que posso dizer é que quando finalmente descobri a existência dele, a ausência de alguma funcionalidade não me agradou...

Hoje em em 2023: Aposto que hoje em dia já devem ter implementado a funcionalidade que eu queria mas não estou em dia com as ferramentas do Mongo para saber.

MongoDB for VS Code

Quando MongoDB for VS Code lançou oficialmente para mim era um sinal que minha busca tinha terminado, pois era uma ferramenta desenvolvida pelos mesmos criadores do MongoDB e que era só uma questão de tempo até implementarem diversas funcionalidades legais.

Nesta época eu até parei o desenvolvimento de uma ferramenta que eu estava construindo.

Hoje em em 2023: Parece que a extensão apenas foi criada para fazer o mínimo e não deve evoluir muito.