Aplicação Desktop Simples em Python com Eel e React: Uma Combinação Poderosa e Fácil de Usar
Recentemente precisei desenvolver uma aplicação desktop para um cliente e queria fazer isso da forma mais direta possível, usando as tecnologias que já conheço, mas sem a necessidade de ter que aprender um novo framework do zero, como o electron. Acabei encontrando a biblioteca eel do Python e fiquei impressionado com a simplicidade e eficácia dela em cumprir o que propõe.
Então, nada melhor do que compartilhar o quão simples é fazer isso!
Basicamente, o que precisaremos fazer é:
1. Instalar o eel
$ pip install eel
2. Criar o script principal, e é tão simples quanto:
# main.py
import eel
# A pasta web é onde ficará nosso projeto React
eel.init("web/src", [".tsx", ".ts", ".jsx", ".js", ".html"])
# Criamos a janela e definimos sua configuração
eel.start({"port": 5173}, host="localhost", port=8080, size=(1000, 600))
# ^-- a porta que está rodando o react
Ok, agora que já temos nosso script criado, vamos iniciar nosso projeto react? bora!!
3. Inicie seu projeto React
Com a queda do create-react-app, por que não usar uma bela alternativa como o Vite? Fique à vontade para iniciar da forma que preferir 👍
$ npm create vite@latest
Ótimo. Script criado e projeto react rodando, nossa estrutura de pasta deverá ficar mais ou menos assim:
E é isso, agora é só executar seu script python main.py
e, se tudo estiver correto, verá uma nova janela aparecendo na sua tela:
Mas agora você deve estar se perguntando:
Isso não é apenas uma janela simples do chrome?
Sim, é exatamente isso. O eel cria um servidor local e expõe arquivos estáticos (HTML, CSS e JS), é basicamente isso.
Então qual o proposito da utilização do eel?
A facilidade de conexão do seu servidor local e sua interface. Veremos agora como você pode "chamar" uma função JavaScript com o Python e vice-versa 🤯
# main.py
import eel
from time import sleep
eel.init("web/src", [".tsx", ".ts", ".jsx", ".js", ".html"])
@eel.expose
def start_count():
count = 0
while True:
eel.setCount(count)
count += 1
sleep(1)
eel.start({"port": 5173}, host="localhost", port=8080, size=(1000, 600))
Criamos nossa função start_count
em Python e deixamos ela acessível ao lado do cliente usando o decorator @eel.expose
. Veja que, ao chamar essa função, ela executará um loop infinito que sempre estará chamando o método eel.setCount
(que iremos criar agora no react).
Agora faremos o mesmo no lado do cliente:
// app.tsx
import { useState } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg'
import './App.css'
export const eel = window.eel
window.eel.set_host("ws://localhost:8080")
function App() {
const [count, setCount] = useState(0)
// expondo o estado para que possa ser acessado pelo python
window.eel.expose(setCount, "setCount")
function handleStartCount() {
window.eel.start_count()
}
return (
<>
<div>
<a href="https://vitejs.dev" target="_blank">
<img src={viteLogo} className="logo" alt="Vite logo" />
</a>
<a href="https://react.dev" target="_blank">
<img src={reactLogo} className="logo react" alt="React logo" />
</a>
</div>
<h1>Vite + React</h1>
<div className="card">
<button onClick={handleStartCount}>Start Count</button>
<div>count is {count}</div>
<p>
Edit <code>src/App.tsx</code> and save to test HMR
</p>
</div>
<p className="read-the-docs">
Click on the Vite and React logos to learn more
</p>
</>
)
}
export default App
E pronto, a setCount
function estará sendo chamada pelo nosso script python e o estado do react é alterado (claro que sabemos que o estado do react não está literalmente sendo alterado pelo python), então teremos esse resultado:
E não precisa se limitar aí, para uma experiencia de uma janela nativa podemos integrar tudo isso ao pywebview para não termos que executar nossa aplicação desktop em uma janela do chrome.