Adiciona skills de Claude Code documentando o aprendizado do gerador de PPTX
- plano-de-ataque: skill de projeto, especifica deste repo (arquivos, formato da planilha, coordenadas de pixel da planta, cores) - pptx-planta-progresso: skill generica/reutilizavel com a metodologia (medicao de eixos em pixel, correcao de crop srcRect, duplicacao de slide em python-pptx, fidelidade de cor, empacotamento sem dependencias para Linux/Windows, validacao sem Office instalado) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
9762709d80
commit
8b15f2d455
163
.claude/skills/plano-de-ataque/SKILL.md
Normal file
163
.claude/skills/plano-de-ataque/SKILL.md
Normal file
@ -0,0 +1,163 @@
|
||||
---
|
||||
name: plano-de-ataque
|
||||
description: Gera ou atualiza o PPTX "Plano de Ataque" (progresso por quinzena de Geotecnia/Estaqueamento, Infraestrutura/Bloco e Superestrutura/Pilar) deste repositório. Use quando o usuário pedir para regenerar o plano de ataque, atualizar quinzenas com uma planilha nova, mudar as cores/legenda do PPTX, ajustar o mapeamento de pixels de uma planta nova, ou reempacotar os pacotes standalone (dist-linux/dist-windows). Escopo: repositório bethania_porto_santos (raiz do projeto).
|
||||
---
|
||||
|
||||
# Plano de Ataque — PPTX de progresso por quinzena
|
||||
|
||||
Este skill resume o que foi aprendido construindo `gerar_plano.py`: um gerador
|
||||
que pega um PPTX-modelo (1 slide de "uma quinzena") e uma planilha de
|
||||
planejamento, e produz um PPTX com 1 slide por quinzena, pintando cada
|
||||
quadrante da planta conforme o status de cada estrutura naquela data.
|
||||
|
||||
## Onde as coisas estão
|
||||
|
||||
- `gerar_plano.py` (raiz) — script-fonte, único arquivo, sem módulos próprios
|
||||
além de `openpyxl`, `python-pptx`, `lxml`.
|
||||
- `cores.json` (raiz) — cores/opacidades/parâmetros de quinzena, editável sem
|
||||
tocar no código (ver seção "Cores" abaixo).
|
||||
- `dist-linux/` — pacote pronto (executável standalone via PyInstaller) +
|
||||
modelo `.pptx` + planilha `.xlsx` de exemplo + `LEIA-ME.txt`.
|
||||
- `dist-windows/` — pacote pronto (Python "embeddable" do python.org com as
|
||||
libs já instaladas em `python/Lib/site-packages`, sem precisar instalar
|
||||
nada) + `PlanoDeAtaque.bat` + mesmos exemplos + `LEIA-ME.txt` + um `.zip`
|
||||
com tudo empacotado.
|
||||
|
||||
## Fluxo normal de uso (regenerar o PPTX)
|
||||
|
||||
1. Reunir em uma pasta: 1 `.json` de cores, 1 `.pptx` modelo (slide 2 = layout
|
||||
de uma quinzena), 1 `.xlsx` de planejamento.
|
||||
2. `python3 gerar_plano.py` dentro dessa pasta (ou rodar o executável/`.bat`
|
||||
em `dist-linux`/`dist-windows`).
|
||||
3. O script AUTO-DETECTA os 3 arquivos pela extensão na própria pasta (não
|
||||
usa nomes fixos) e gera `<nome-do-modelo>_ATUALIZADO.pptx`.
|
||||
4. Se o usuário mandar uma planilha/modelo NOVO por upload, é só copiar para
|
||||
`.uploads/` (ou onde estiver rodando), gerar, e comparar visualmente
|
||||
(ver "Como validar" abaixo) antes de entregar.
|
||||
|
||||
## Modelo de dados da planilha
|
||||
|
||||
Aba **"Planejamento"**, dados a partir da linha 4:
|
||||
- Coluna B: nome do quadrante, formato `"<col1>-<col2>/<linha1>-<linha2>"`,
|
||||
ex. `"10-11/A1-B"` (colunas são números 1-48; linhas são rótulos entre
|
||||
`D, C, B1, B, A1, A`). Regex usado: `^(\d+)-(\d+)/([A-Z0-9]+)-([A-Z0-9]+)$`.
|
||||
- Colunas C/D: Geotecnia (Início/Término)
|
||||
- Colunas E/F: Infraestrutura (Início/Término)
|
||||
- Colunas G/H: Superestrutura (Início/Término)
|
||||
|
||||
Status por quadrante/estrutura numa data de corte `p_end`:
|
||||
- `None` (não pintar) se Início vazio ou Início > p_end
|
||||
- `"concluido"` se Término preenchido e Término <= p_end
|
||||
- `"andamento"` caso contrário
|
||||
|
||||
## Quinzenas
|
||||
|
||||
Períodos de **14 dias** começando em `2026-12-14 = Quinzena 02` (a planilha
|
||||
não tem atividade antes dessa data — por isso a numeração já começa em 02,
|
||||
não em 01). Esses três parâmetros ficam em `cores.json → quinzena` e dão
|
||||
para editar sem mexer no código: `duracao_dias`, `data_inicio`,
|
||||
`numero_inicial`.
|
||||
|
||||
## Cores (`cores.json`)
|
||||
|
||||
Uma seção por estrutura (`geotecnia`, `superestrutura`, `infraestrutura`),
|
||||
cada uma com `andamento` e `concluido`, cada um com `preenchimento` (hex sem
|
||||
`#`), `borda` (hex sem `#`) e `alpha_preenchimento` (0-100). A legenda do
|
||||
slide sempre usa 100% de opacidade (fiel à cor pedida); o desenho usa o
|
||||
`alpha_preenchimento` configurado — mais baixo na geotecnia (cobre o
|
||||
quadrante inteiro, não pode esconder a planta) e mais alto nos marcadores de
|
||||
pilar/bloco (são pequenos, não atrapalham).
|
||||
|
||||
**Regra de ouro ao pedir "mude a cor X pra Y"**: sempre pedir/usar um print
|
||||
em ALTA resolução da legenda e fazer amostragem de pixel (não confiar em
|
||||
screenshot comprimido/pequeno — já erramos assim uma vez nesta conversa).
|
||||
Amostrar tanto o preenchimento quanto a BORDA (são cores diferentes, e a
|
||||
legenda de referência sempre tem as duas).
|
||||
|
||||
## Como as camadas são desenhadas (importante para não perder fidelidade)
|
||||
|
||||
- **Geotecnia/Estaqueamento**: retângulo arredondado cobrindo o quadrante
|
||||
INTEIRO (não um recorte menor).
|
||||
- **Superestrutura/Pilar e Infraestrutura/Bloco**: NÃO são um retângulo
|
||||
proporcional ao quadrante — são marcadores pequenos (quadrado arredondado,
|
||||
`marcador_fracao` do espaçamento entre colunas) plantados nos 4 VÉRTICES
|
||||
(colunaxlinha) de cada quadrante ativo. Como vértices são compartilhados
|
||||
entre quadrantes vizinhos, dedupe por ponto usando o status mais avançado
|
||||
(`concluido` > `andamento`) para não desenhar marcadores duplicados/
|
||||
sobrepostos no mesmo lugar.
|
||||
|
||||
## Mapeamento pixel → EMU (a parte mais delicada)
|
||||
|
||||
As posições de colunas/linhas da planta (`IM1_COLPX`, `IM2_COLPX`,
|
||||
`IM1_ROWPX`, `IM2_ROWPX` no script) são constantes medidas a dedo nas imagens
|
||||
do modelo, usando detecção de blobs por soma de pixels escuros por
|
||||
coluna/linha (círculos dos eixos, ver histórico da conversa para o script de
|
||||
medição). Se a planta do modelo mudar, essas constantes têm que ser
|
||||
remedidas — **sempre medir direto na imagem que está DE FATO embutida no
|
||||
`.pptx`** (`ppt/media/imageN.png` dentro do zip), não em uma cópia enviada
|
||||
separadamente por upload: já tivemos um bug de desalinhamento porque as duas
|
||||
não eram pixel-idênticas.
|
||||
|
||||
**Gotcha crítico**: se a imagem no slide tiver um corte aplicado no
|
||||
PowerPoint (`<a:srcRect t="..." b="..." l="..." r=".../>` dentro do
|
||||
`<p:blipFill>`), a conversão pixel→EMU tem que descontar esse corte, senão
|
||||
tudo fica deslocado (geralmente para baixo/lado). Sempre checar o XML do
|
||||
`<p:pic>` de cada imagem do slide-modelo antes de assumir que pixel 0 =
|
||||
canto da imagem exibida. Foi exatamente isso que causou o "a imagem de baixo
|
||||
está deslocada" nesta conversa.
|
||||
|
||||
## Duplicar slides em python-pptx
|
||||
|
||||
Não existe API pública para duplicar slide. A receita usada:
|
||||
1. `dest = prs.slides.add_slide(source.slide_layout)`, depois remover os
|
||||
placeholders que a layout injeta.
|
||||
2. Para cada relationship de imagem do slide-fonte, `dest.part.relate_to(...)`
|
||||
para criar a relação equivalente no slide-destino (isso gera um rId novo,
|
||||
que pode não bater com o rId original).
|
||||
3. Deep-copy de cada shape do slide-fonte e **reescrever os atributos
|
||||
`r:embed`/`r:link` diretamente no elemento XML** (`el.set(qn('r:embed'), novo_rid)`),
|
||||
nunca com substituição de texto/regex na string do XML — se o
|
||||
remapeamento de rIds for uma permutação (ex. rId2↔rId3), um replace
|
||||
sequencial de string corrompe os dois (bug real que já apareceu aqui).
|
||||
|
||||
## Reempacotar os executáveis
|
||||
|
||||
- **Linux** (`dist-linux/PlanoDeAtaque`): `pip install openpyxl python-pptx
|
||||
lxml pyinstaller` e `pyinstaller --onefile --name PlanoDeAtaque
|
||||
gerar_plano.py`.
|
||||
- **Windows** (`dist-windows/`): este ambiente não tem Wine nem acesso a uma
|
||||
máquina Windows, então NÃO dá para compilar um `.exe` de verdade aqui. A
|
||||
solução usada foi montar um Python "portátil": baixar o
|
||||
`python-3.11.9-embed-amd64.zip` oficial do python.org, habilitar
|
||||
site-packages editando `python311._pth` (adicionar `Lib\site-packages` e
|
||||
`import site`), e usar `pip download --platform win_amd64 --python-version
|
||||
311 --implementation cp --abi cp311 --only-binary=:all: <pacotes>` para
|
||||
baixar as wheels certas (mesmo rodando de um Linux — `pip download` só
|
||||
baixa o arquivo, não executa nada) e extrair (unzip) cada wheel dentro de
|
||||
`python/Lib/site-packages`. Lançador é um `.bat` simples chamando
|
||||
`python\python.exe gerar_plano.py`. Depois de montar, gerar de novo o
|
||||
`PlanoDeAtaque_windows.zip` (cuidado: gerar o zip DENTRO da própria pasta
|
||||
que está sendo zipada faz o zip se autoincluir — gerar em `/tmp` e mover
|
||||
para dentro depois).
|
||||
|
||||
## Como validar antes de entregar
|
||||
|
||||
Como não há LibreOffice/PowerPoint disponível neste ambiente para renderizar
|
||||
o `.pptx`, a validação usada foi:
|
||||
1. Integridade: `zipfile.testzip()` + `lxml.etree.fromstring` em cada
|
||||
`.xml`/`.rels` do pacote.
|
||||
2. Reconstrução visual manual: ler `off/ext` (EMU) de cada `<p:pic>` e forma
|
||||
colorida do slide via `python-pptx`, converter para pixels e compor com
|
||||
Pillow (`Image.alpha_composite`) para gerar um PNG de conferência — é
|
||||
assim que os desalinhamentos e cores erradas foram detectados nesta
|
||||
conversa.
|
||||
|
||||
## Git neste repositório
|
||||
|
||||
Não existe identidade de git configurada globalmente por padrão aqui. Em vez
|
||||
de rodar `git config --global` (proibido pelas regras de segurança do
|
||||
ambiente), usar override só para o comando do commit:
|
||||
`git -c user.name="..." -c user.email="..." commit -m "..."`.
|
||||
`.uploads/` está no `.gitignore` (são anexos de conversa, não arquivos do
|
||||
projeto) — os artefatos finais que importam ficam em `dist-linux/` e
|
||||
`dist-windows/`.
|
||||
241
.claude/skills/pptx-planta-progresso/SKILL.md
Normal file
241
.claude/skills/pptx-planta-progresso/SKILL.md
Normal file
@ -0,0 +1,241 @@
|
||||
---
|
||||
name: pptx-planta-progresso
|
||||
description: Metodologia para construir um gerador de PPTX de progresso de obra (ou qualquer overlay colorido sobre uma planta/imagem técnica) a partir de uma imagem de planta com grelha de eixos, uma planilha de cronograma e um PPTX-modelo. Use quando o usuário pedir para pintar/marcar status (andamento/concluído) sobre uma planta em um PowerPoint, gerar um slide por período (quinzena/semana/mês) a partir de um cronograma, ou reproduzir esse tipo de relatório visual em um projeto novo (edificação, planta, cronograma diferentes dos já usados).
|
||||
---
|
||||
|
||||
# Overlay de progresso sobre planta técnica em PPTX
|
||||
|
||||
Metodologia genérica extraída de um caso real (planta de fundações dividida
|
||||
em quadrantes, cronograma por Geotecnia/Infraestrutura/Superestrutura,
|
||||
1 slide por quinzena). Os nomes aqui são genéricos de propósito — adapte aos
|
||||
nomes reais do projeto em mãos.
|
||||
|
||||
Esta skill é sobre **método**, não sobre um script fixo: os detalhes (nomes
|
||||
de coluna, quantidade de eixos, cores) sempre vão mudar de projeto para
|
||||
projeto; o que se repete é a sequência de passos e as pegadinhas abaixo.
|
||||
|
||||
## Visão geral do pipeline
|
||||
|
||||
```
|
||||
imagem(ns) da planta ──┐
|
||||
├─→ medir pixels dos eixos → mapear quadrante→retângulo
|
||||
planilha de cronograma ─┘ │
|
||||
▼
|
||||
PPTX-modelo (1 slide-tipo) ──→ duplicar N vezes (1 por período) ──→ pintar
|
||||
cada
|
||||
quadrante
|
||||
conforme
|
||||
status
|
||||
```
|
||||
|
||||
## Passo 1 — Medir os eixos da planta em pixels
|
||||
|
||||
Se a planta tem uma grelha de eixos numerados/rotulados (comum em plantas de
|
||||
estrutura/fundação: colunas numéricas, linhas em letras), NÃO estime a
|
||||
posição visualmente — meça programaticamente:
|
||||
|
||||
1. Carregar a imagem em escala de cinza (`PIL` + `numpy`).
|
||||
2. Binarizar (`pixel < limiar`) e restringir a análise a uma FAIXA estreita
|
||||
onde só existem os círculos/marcas de eixo (topo da imagem para colunas,
|
||||
margem esquerda para linhas) — se usar a imagem inteira, hachuras e texto
|
||||
de dentro do desenho viram ruído e atrapalham a detecção.
|
||||
3. Somar pixels escuros por coluna (ou por linha) dentro dessa faixa.
|
||||
4. Agrupar posições consecutivas com valor > 0 (tolerância de alguns pixels
|
||||
de "buraco") em clusters; o centro de cada cluster (média ponderada pela
|
||||
intensidade) é a posição do eixo.
|
||||
5. Filtrar clusters espúrios por LARGURA — um círculo de eixo tem uma largura
|
||||
característica (~mesma para todos); qualquer cluster muito mais estreito
|
||||
ou muito mais largo é ruído (seta, cota, texto cruzando a faixa) e deve
|
||||
ser descartado. Conferir sempre que o número de clusters batidos é igual
|
||||
ao número de eixos esperado.
|
||||
6. Fazer o mesmo para linhas, cuidado com textos de cota grandes/rotacionados
|
||||
que caem na mesma faixa (ex. uma cota vertical entre dois eixos pode gerar
|
||||
um cluster fantasma — filtrar por largura resolve isso também).
|
||||
|
||||
Sempre medir na imagem **de fato usada no PPTX final** (extraia
|
||||
`ppt/media/imageN.png` de dentro do `.zip` do `.pptx`, não confie numa cópia
|
||||
enviada separadamente — arquivos "iguais" podem ter poucos pixels de
|
||||
diferença de crop/exportação e isso já causou desalinhamento em produção).
|
||||
|
||||
## Passo 2 — Mapear "quadrante" (ou elemento) → retângulo/ponto em pixels
|
||||
|
||||
Se o cronograma se refere a células/trechos da planta por um código (ex.
|
||||
`"10-11/A1-B"` = colunas 10 a 11, linhas A1 a B), parseie esse código com
|
||||
regex e resolva os 2 pares de coordenadas usando os dicionários do passo 1.
|
||||
Se a planta estiver dividida em mais de uma imagem (ex. planta muito larga
|
||||
exportada em 2 metades), identifique de qual imagem cada elemento faz parte
|
||||
(geralmente por faixa de coluna) — mas confirme que a granularidade do
|
||||
cronograma nunca faz um elemento "atravessar" duas imagens; se atravessar,
|
||||
esse caso precisa de tratamento especial.
|
||||
|
||||
## Passo 3 — Pixel → coordenada do PPTX (EMU)
|
||||
|
||||
Cada imagem colocada num slide tem uma posição/tamanho (`off`/`ext`, em EMU)
|
||||
no XML do PPTX. A conversão ingênua é interpolação linear:
|
||||
`emu = off + (pixel / tamanho_da_imagem_em_px) * ext`.
|
||||
|
||||
**Gotcha crítico**: se a imagem tiver um corte aplicado no PowerPoint
|
||||
(elemento `<a:srcRect t="…" b="…" l="…" r="…"/>` dentro do `<p:blipFill>`),
|
||||
a interpolação tem que ser feita sobre a região VISÍVEL da imagem, não sobre
|
||||
a imagem inteira — do contrário tudo fica deslocado (geralmente para baixo
|
||||
ou para o lado que foi cortado). Sempre inspecionar o XML de cada `<p:pic>`
|
||||
do slide-modelo antes de assumir que pixel 0 = canto exibido:
|
||||
|
||||
```python
|
||||
import re
|
||||
xml = open('slide_extraido.xml').read()
|
||||
for m in re.finditer(r'<p:pic>.*?</p:pic>', xml, re.S):
|
||||
print(re.search(r'name="([^"]+)"', m.group(0)).group(1),
|
||||
re.search(r'<a:srcRect[^/]*/>', m.group(0)))
|
||||
```
|
||||
|
||||
Se houver corte, a fórmula fica:
|
||||
```
|
||||
vis0 = corte_inicio_fraction * tamanho_px
|
||||
vis1 = (1 - corte_fim_fraction) * tamanho_px
|
||||
escala = ext / (vis1 - vis0)
|
||||
emu = off + (pixel - vis0) * escala
|
||||
```
|
||||
|
||||
Se a planta estiver em mais de uma imagem lado a lado/empilhada, ainda dá
|
||||
para conferir que a escala px→EMU bate entre elas (compare a distância entre
|
||||
dois eixos conhecidos em cada imagem, convertida para EMU) — se bater, dá
|
||||
para usar um offset constante para alinhar uma imagem à outra visualmente
|
||||
mesmo que tenham sido exportadas com recortes/margens diferentes.
|
||||
|
||||
## Passo 4 — Decidir COMO pintar cada tipo de status
|
||||
|
||||
Nem todo "status" deve ser um retângulo do tamanho do quadrante inteiro.
|
||||
Dois padrões comuns, que podem coexistir na mesma planta:
|
||||
|
||||
- **Status de área** (ex. escavação/estaqueamento, algo que ocupa a área
|
||||
toda do trecho): retângulo cobrindo o quadrante inteiro, com transparência
|
||||
(alpha < 100%) para a planta de baixo continuar legível.
|
||||
- **Status de ponto** (ex. pilar, bloco de fundação — elementos que existem
|
||||
em pontos discretos, normalmente nos vértices/interseções da grelha, não
|
||||
espalhados pela área): marcador pequeno (quadrado/círculo) centrado em
|
||||
cada vértice relevante, não um retângulo proporcional ao quadrante. Se um
|
||||
vértice é compartilhado por elementos vizinhos, faça DEDUPE por ponto
|
||||
(usar o status mais avançado entre os que compartilham aquele vértice —
|
||||
ex. "concluído" prevalece sobre "andamento") em vez de desenhar
|
||||
marcadores sobrepostos.
|
||||
|
||||
Peça sempre uma imagem de referência de como o cliente já visualiza isso
|
||||
(ou já visualizou em outra ferramenta) antes de assumir qual dos dois
|
||||
padrões usar — a diferença visual é grande e already causou retrabalho.
|
||||
|
||||
## Passo 5 — Fidelidade de cor
|
||||
|
||||
Se o usuário fornecer uma legenda de referência (imagem), AMOSTRE OS PIXELS
|
||||
em vez de "chutar" a cor no olho — e use a imagem em maior resolução
|
||||
disponível (uma captura de tela pequena/comprimida pode alterar o tom o
|
||||
suficiente pra ficar "quase certo, mas não fiel", que foi motivo de reclamação
|
||||
real numa entrega). Amostre separadamente o preenchimento (centro do ícone) e
|
||||
a borda (poucos pixels para dentro da borda visível) — normalmente são cores
|
||||
diferentes.
|
||||
|
||||
```python
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
arr = np.array(Image.open('legenda.png').convert('RGB'))
|
||||
patch = arr[y-5:y+5, x-5:x+5].reshape(-1, 3)
|
||||
hexcolor = '#%02X%02X%02X' % tuple(patch.mean(axis=0).round().astype(int))
|
||||
```
|
||||
|
||||
Exponha as cores (preenchimento, borda, opacidade) num **arquivo de
|
||||
configuração separado do código** (JSON simples) para o cliente poder ajustar
|
||||
sem depender de você re-editar script.
|
||||
|
||||
## Passo 6 — Duplicar o slide-modelo N vezes (1 por período)
|
||||
|
||||
`python-pptx` não tem API pública para duplicar slide. Receita:
|
||||
|
||||
```python
|
||||
def duplicate_slide(prs, source_index):
|
||||
source = prs.slides[source_index]
|
||||
dest = prs.slides.add_slide(source.slide_layout)
|
||||
for shp in list(dest.shapes):
|
||||
shp._element.getparent().remove(shp._element) # limpa placeholders da layout
|
||||
|
||||
rid_map = {}
|
||||
for rId, rel in source.part.rels.items():
|
||||
if rel.reltype == RT.SLIDE_LAYOUT or rel.is_external:
|
||||
continue
|
||||
rid_map[rId] = dest.part.relate_to(rel.target_part, rel.reltype)
|
||||
|
||||
for shp in source.shapes:
|
||||
new_el = copy.deepcopy(shp._element)
|
||||
# IMPORTANTE: reescrever r:embed/r:link direto no atributo XML,
|
||||
# nunca com replace de string — se o remapeamento de rIds for uma
|
||||
# permutação (ex. rId2 vira rId3 e rId3 vira rId2), um replace
|
||||
# sequencial de texto corrompe as duas referências.
|
||||
for el in new_el.iter():
|
||||
for attr in (qn('r:embed'), qn('r:link')):
|
||||
old = el.get(attr)
|
||||
if old in rid_map:
|
||||
el.set(attr, rid_map[old])
|
||||
dest.shapes._spTree.append(new_el)
|
||||
return dest
|
||||
```
|
||||
|
||||
Depois de duplicar, reordenar slides manipulando `prs.slides._sldIdLst`
|
||||
diretamente (mover/remover elementos `<p:sldId>`) já que também não há API
|
||||
pública para isso.
|
||||
|
||||
## Passo 7 — Validar sem PowerPoint/LibreOffice instalado
|
||||
|
||||
Quando não há como renderizar o `.pptx` de verdade:
|
||||
|
||||
1. **Integridade estrutural**: `zipfile.ZipFile(path).testzip()` (sem
|
||||
corrupção) + `lxml.etree.fromstring` em cada `.xml`/`.rels` do pacote
|
||||
(sem erro de parse).
|
||||
2. **Reconstrução visual manual**: ler `left/top/width/height` (EMU) de cada
|
||||
`<p:pic>` e forma via `python-pptx`, converter para pixels, montar um PNG
|
||||
com Pillow (`Image.alpha_composite`) reproduzindo o slide. É assim que se
|
||||
detectam desalinhamentos e cores erradas ANTES de entregar — não confie
|
||||
só na leitura do XML.
|
||||
|
||||
## Passo 8 — Empacotar como "sem dependências" para o usuário final
|
||||
|
||||
- **Linux**: `pyinstaller --onefile --name NomeDoApp script.py` — direto.
|
||||
- **Windows, sem acesso a uma máquina/VM Windows nem Wine**: não dá para
|
||||
compilar um `.exe` de verdade. Alternativa que funciona: montar um Python
|
||||
"portátil" —
|
||||
1. Baixar o zip *embeddable* oficial do python.org
|
||||
(`python-<versão>-embed-amd64.zip`).
|
||||
2. Editar o `python<XY>._pth` de dentro do zip para incluir
|
||||
`Lib\site-packages` e descomentar `import site` (por padrão o
|
||||
embeddable vem sem suporte a pacotes de terceiros).
|
||||
3. Baixar as wheels certas **mesmo rodando de Linux**, sem precisar de
|
||||
Wine, com `pip download --platform win_amd64 --python-version <XY>
|
||||
--implementation cp --abi cp<XY> --only-binary=:all: <pacotes>`
|
||||
(`pip download` só baixa o arquivo, não executa nada — funciona
|
||||
cross-platform).
|
||||
4. Extrair (unzip) cada `.whl` dentro de `Lib\site-packages`.
|
||||
5. Lançador: um `.bat` de uma linha chamando
|
||||
`python\python.exe script.py`, com `pause` no final pra não fechar a
|
||||
janela sozinho.
|
||||
- Deixe claro pro usuário que isso é uma alternativa válida a um `.exe`
|
||||
"de verdade" — funciona igual (zero instalação), mas é uma pasta em vez
|
||||
de um arquivo único.
|
||||
- Se for gerar um `.zip` desse pacote, gere em um diretório TEMPORÁRIO
|
||||
fora da pasta que está sendo zipada, e só depois mova o `.zip` para
|
||||
dentro — gerar direto dentro da própria pasta faz o zip se
|
||||
autoincluir (bug real, já aconteceu).
|
||||
|
||||
## Passo 9 — Descoberta automática de arquivos de entrada
|
||||
|
||||
Se o programa final vai rodar numa pasta com 2-3 arquivos de entrada
|
||||
(config, modelo, dados), não force nomes fixos — detecte por extensão
|
||||
(`glob.glob('*.xlsx')` etc.), e falhe com mensagem clara se encontrar 0 ou
|
||||
mais de 1 candidato ("deixe só um arquivo .xlsx na pasta"). Isso deixa o
|
||||
programa reutilizável em outro projeto só trocando os arquivos de entrada,
|
||||
sem precisar editar nada.
|
||||
|
||||
## Nota sobre git neste tipo de ambiente sandbox
|
||||
|
||||
Ambientes de execução isolados (containers efêmeros) às vezes não têm
|
||||
identidade de git configurada e não se deve rodar `git config --global`
|
||||
(pode ser bloqueado por política, e além disso não é uma boa prática mexer
|
||||
em config global do usuário). Use override só no comando do commit:
|
||||
`git -c user.name="..." -c user.email="..." commit -m "..."`.
|
||||
Loading…
Reference in New Issue
Block a user