Framework de UI desktop (PySide6) com janela frameless, roteamento e temas
Project description
UI Exec Framework (PySide6)
Base moderna para aplicativos desktop com Python + PySide6. Inclui janela frameless, roteamento hierárquico, sistema de temas (QSS + JSON) com animação, toasts e Centro de Notificações, além de um CLI para produtividade.
Sumário
- Visão Geral
- Instalacao (PyPI) e uso rapido
- Arquitetura
- Instalação e Setup
- Execução e Atalhos
- Páginas e Roteamento
- Temas (QSS + JSON)
- Toasts e Notificações
- CLI
- Criação Manual de Páginas
- Criação Manual de Subpáginas
- Widgets: Guia Prático
- Personalização Visual
- Centro de Notificações (Avançado)
- Troubleshooting
- Boas Práticas
- Roadmap
- Blueprint de Página (modelo recomendado)
- Qualidade de Código (Linting & Style)
Instalacao (PyPI) e uso rapido
Instale a biblioteca (ou atualize):
pip install ui-fram-skk
# ou
pip install -U ui-fram-skk
Comece um projeto novo e rode:
# 1) Inicialize o esqueleto de app (cria app/, assets/, pages/, etc.)
ui-cli init
# 2) Rode o app gerado
python -m app.app
# 3) (Opcional) Gere paginas com o CLI
ui-cli new page "Relatorios" --route relatorios --label "Relatorios" --sidebar --order 10
Ajuda rapida do CLI:
ui-cli --help
Visão Geral
Principais recursos
- Janela frameless com sombra e cantos arredondados.
- Roteamento de páginas com auto‑descoberta e manifesto.
- Temas dinâmicos via QSS + JSON, com animação suave entre temas.
- Quick Open (Ctrl+K) para navegar por rotas rapidamente.
- TopBar com breadcrumb clicável e sino com badge de não lidas.
- Centro de Notificações (push à direita) integrado aos toasts.
- Toasts simples, com ações e com progresso.
- Persistência de tema e última rota em cache JSON.
- CLI para scaffolding, exemplos, limpeza e manifesto.
- Injeção de dependências (task_runner, theme_service) em páginas. Novidades
- Router v2: rotas hierárquicas (
home/ferramentas/detalhes), histórico (Alt+Left/Alt+Right),routeChangede hookon_route(params)por página. - Quick Open (Ctrl+K) via
ui/dialogs/quick_open.py. - Centro de Notificações com PushSidePanel e
notification_bus(). - ThemeService: watcher de
base.qsse temas, tokens derivados e dump do QSS aplicado. - TitleBar com ícone animado (cross‑fade) e sincronização com o tema.
- TaskRunnerAdapter aceita
async def run_task(...).
Arquitetura
Mapa de diretórios
app/
app.py
settings.py
assets/
icons/ (app/, client/)
qss/
themes/
cache/
pages/
base_page.py
home_page.py
notificacoes.py
registry.py
settings.py
subpages/
guia_rapido_page.py
theme_editor.py
ui/
core/
app.py
app_controller.py
command_bus.py
frameless_window.py
interface_ports.py
main.py
router.py
settings.py
theme_service.py
toast_manager.py
utils/ (factories.py, guard.py, paths.py)
dialogs/ (quick_open.py)
services/ (qss_renderer.py, task_runner_adapter.py, theme_repository_json.py)
splash/ (splash.py)
widgets/ (async_button.py, buttons.py, loading_overlay.py,
overlay_sidebar.py, push_sidebar.py, settings_sidebar.py,
titlebar.py, toast.py, topbar.py)
requirements.txt
license
README.md
Execução e Atalhos
Executar
python -m ui.core.main
# Alternativas
python -m app.app
python ui/core/main.py
Configurações (app/settings.py)
APP_TITLE = "Meu App"
DEFAULT_THEME = "Aku"
FIRST_PAGE = "home"
Atalhos de teclado
- Alt+Left / Alt+Right: histórico do Router (voltar/avançar).
- Ctrl+K: Quick Open (busca/abre rotas).
- Ctrl+M: minimizar com animação.
- Ctrl+Enter: maximizar/restaurar.
Páginas e Roteamento
Manifesto (app/assets/pages_manifest.json)
[
{ "route": "home", "label": "Início", "sidebar": true, "order": 0,
"factory": "app.pages.home_page:build" }
]
Auto‑descoberta (app/pages)
Qualquer módulo com PAGE e build(...) é detectado. Exemplo:
PAGE = { "route": "relatorios", "label": "Relatórios", "sidebar": True, "order": 10 }
def build(task_runner=None, theme_service=None) -> QWidget:
...
Injeção de dependências
task_runner: executa tarefas de negócio (I/O, rede, etc.).theme_service: reage a trocas de tema e fornece tokens. Recursos do Routerrouter.go(path, params={})navega para uma rota (com/).- Sinal
routeChanged(path, params)para persistência/breadcrumb/log. - Histórico:
go_back()/go_forward()(Alt+Left/Alt+Right). - Hook por página:
on_route(self, params: dict).
Temas (QSS + JSON)
Estrutura
- QSS base:
app/assets/qss/base.qss - JSON de variáveis:
app/assets/themes/*.jsonExemplo
{
"vars": { "bg": "#1a1a1a", "text": "#ffffff", "accent": "#4285f4" }
}
ThemeService
- Anima troca de tema (com ou sem animação).
- Observa
base.qsse a pasta de temas (hot‑reload). - Emite
themeApplied,themesChanged,themeTokensChanged. - Persiste tema em
app/assets/cache/_ui_exec_settings.json. Editor e painel - Painel (engrenagem na TitleBar) para selecionar/criar/editar/excluir temas.
- Persistência via
JsonThemeRepository(gravação atômica). - Ícone do app é sincronizado com o tema ativo. QSS placeholders (qss_renderer)
{{token}},{token},${token}com defaults e derivados úteis (ex.:content_bg).
Toasts e Notificações
APIs
show_toast(parent, text, kind="info", timeout_ms=2400, persist=False)show_action_toast(parent, title, text, kind="info", actions=[...], sticky=False, timeout_ms=3200, persist=False)ProgressToast.start(parent, text, kind="info", cancellable=True)(comupdate(...)/finish(...))notification_bus()para integração com o Centro. Centro de Notificações (push à direita)- Abre/fecha pela TopBar (sino) e exibe entradas persistidas.
- Toasts dispensados podem virar entradas do Centro (título/texto/flags/ações).
- Largura redimensionável por “grip”, badge de não lidas na TopBar. Exemplo
from ui.widgets.toast import (
show_toast, show_action_toast, ProgressToast, notification_bus
)
show_toast(parent, text="Ação concluída!", kind="info")
show_action_toast(
parent,
title="Exportação",
text="Arquivo gerado com sucesso.",
kind="ok",
actions=[{"label": "Abrir pasta", "command": "abrir_pasta", "payload": {"path": "..."}}],
persist=True,
)
pt = ProgressToast.start(parent, "Carregando dados...", kind="info", cancellable=True)
pt.update(5, 10)
pt.finish(True, "Processo finalizado!")
CLI
Ferramentas para gerar páginas, criar exemplos, atualizar o manifesto e limpar o projeto. Operam em app/pages e mantêm app/assets/pages_manifest.json alinhado.
new page
- O que faz: cria uma página padrão com
PAGE,build(), hookon_route()e estrutura comQScrollAreapronta para conteúdos longos. - Uso rápido (bash):
# 1) Cria página "Relatórios" com rota e label configurados
python -m ui.cli new page "Relatorios" \
--route relatorios \
--label "Relatórios" \
--order 10 \
--sidebar
# 2) (Opcional) Sobrescrever arquivo se já existir
python -m ui.cli new page "Relatorios" --route relatorios --force
- Resultado: arquivo
app/pages/relatorios_page.pycriado e entrada adicionada/atualizada no manifesto.
new subpage
- O que faz: cria uma subpágina (rota
pai/filho, ex.:home/detalhes). Útil para seções internas. - Uso (bash):
# Cria uma subpágina "Detalhes" sob a rota pai "home"
python -m ui.cli new subpage "Detalhes" \
--parent home \
--route detalhes \
--label "Detalhes" \
--order 11 \
--sidebar
- Resultado: arquivo
app/pages/home_detalhes_page.py(nome normalizado) e manifesto atualizado.
examples
- O que faz: gera
examples_widgets_page.pycom usos práticos (botões, AsyncTaskButton, toasts) para consulta. - Uso (bash):
python -m ui.cli examples
- Resultado: rota
examplesadicionada ao manifesto para navegação rápida.
manifest-update
- O que faz: reescreve
app/assets/pages_manifest.jsoncom base na auto‑descoberta deapp.pages.*(lendoPAGEebuild()). - Uso (bash):
python -m ui.cli manifest-update
- Dica: use após mover/renomear módulos manualmente para sincronizar o manifesto.
clean-pages
- O que faz: remove páginas de exemplo e recria uma Home mínima (rota
home). - Uso (bash):
# Limpeza e criação da Home mínima
python -m ui.cli clean-pages
# (Opcional) Reconstruir manifesto via auto‑descoberta logo após a limpeza
python -m ui.cli clean-pages --rebuild-manifest
- Resultado: projeto enxuto para começar; somente a Home permanece.
Widgets Inclusos
- TitleBar: barra de título com ícone animado, botões min/max/close e engrenagem.
- TopBar: breadcrumb clicável, sino com badge e menu.
- OverlaySidePanel: menu lateral (overlay) com scrim.
- SettingsSidePanel: painel de configurações (direita).
- PushSidePanel: painel lateral (Centro de Notificações), redimensionável.
- LoadingOverlay: overlay de carregamento sensível ao tema (GIF).
- Toast / ActionToast / ProgressToast: notificações flutuantes integradas ao Centro.
- AsyncButton: execução assíncrona com feedback visual.
- QuickOpenDialog: busca/abre rotas (Ctrl+K).
Tutoriais Rápidos
Criar página
python -m ui.cli new page "Financeiro" --route financeiro --sidebar
Gerada com PAGE, build() e QScrollArea; build(task_runner=None, theme_service=None) recebe dependências injetadas.
Criar tema
- Duplique
app/assets/themes/Dark.json. - Renomeie (ex.:
MyTheme.json) e ajuste cores. - Selecione pelo painel de configurações (engrenagem na TitleBar). Integrar TaskRunner
from ui.services.task_runner_adapter import TaskRunnerAdapter
class MyRunner:
def run_task(self, name, payload):
if name == "calcular_relatorio":
return {"ok": True, "data": "Relatório gerado"}
return {"ok": False, "error": "Ação inválida"}
controller = AppController(task_runner=TaskRunnerAdapter(MyRunner()))
Boas Práticas
- Centralize estilos no
base.qss; evitesetStyleSheetdireto. - Separe UI de lógica de negócio (via
task_runner). - Padronize rotas (kebab‑case) e labels claros.
- Use
theme_servicepara cores/tokens e reatividade visual. - Mantenha a estrutura de pastas limpa para facilitar autoload.
Roadmap
- Subpáginas e histórico de navegação aprimorados.
- Centro de notificações persistente/filtrável.
- Logger de eventos integrável.
- Pesquisa global e atalhos adicionais.
- Gerenciador de estado leve (Qt Signals).
- Empacotamento (PyInstaller/Briefcase).
Instalação e Setup
- Requisitos
- Python 3.10+
- Pip recente e ambiente virtual recomendado
- Windows/macOS/Linux com Qt (PySide6)
- Passo a passo
- Crie e ative um ambiente virtual
- Windows:
python -m venv .venv && .venv\Scripts\activate - macOS/Linux:
python -m venv .venv && source .venv/bin/activate
- Windows:
- Instale dependências:
pip install -r requirements.txt - Rode o app:
python -m ui.core.main
- Crie e ative um ambiente virtual
CLI Detalhado (guia completo)
O CLI acelera tarefas comuns. Abaixo, as opções e impactos de cada comando.
- new page
- Cria um arquivo em
app/pages/<rota>_page.pycom:PAGE = { route, label, sidebar, order }build(task_runner=None, theme_service=None)- Estrutura com
QScrollAreapronta para conteúdo extenso
- Exemplo:
python -m ui.cli new page "Relatorios" --route relatorios --label Relatórios --order 10 --sidebar
- Manifesto: faz “upsert” de uma entrada coerente em
app/assets/pages_manifest.json.
- Cria um arquivo em
- new subpage
- Gera uma subpágina com rota
pai/filho(ex.:home/detalhes). - Exemplo:
python -m ui.cli new subpage "Detalhes" --parent home --route detalhes --label Detalhes --order 11 --sidebar
- Arquivo:
app/pages/home_detalhes_page.py(rota normalizada). - Manifesto: atualiza com a nova rota.
- Gera uma subpágina com rota
- examples
- Cria
examples_widgets_page.pycom usos práticos de botões, AsyncTaskButton e toasts. - Exemplo:
python -m ui.cli examples - Manifesto: adiciona rota
examples.
- Cria
- manifest-update
- Reescreve completamente
app/assets/pages_manifest.jsoncom base na auto‑descoberta deapp.pages.*. - Útil após mover/renomear páginas manualmente.
- Exemplo:
python -m ui.cli manifest-update
- Reescreve completamente
- clean-pages
- Remove páginas pré‑programadas de exemplo e cria uma Home mínima.
- Exemplo:
python -m ui.cli clean-pages(ou--rebuild-manifestpara reconstruir o manifesto via descoberta) Observações importantes
- O nome do arquivo gerado é a rota com barras substituídas por sublinhados +
_page.py. - A descoberta padrão importa
app.pages.*; mantenha páginas dentro deapp/pages.
Criando Páginas Manualmente (sem CLI)
- Crie um arquivo em
app/pages/minha_pagina_page.pycom:
from PySide6.QtWidgets import QWidget, QVBoxLayout, QLabel, QScrollArea, QFrame
PAGE = { "route": "minha-pagina", "label": "Minha Página", "sidebar": True, "order": 50 }
class MinhaPaginaPage(QWidget):
def __init__(self, task_runner=None, theme_service=None):
super().__init__()
root = QVBoxLayout(self); root.setContentsMargins(14,14,14,14); root.setSpacing(12)
scroll = QScrollArea(self); scroll.setWidgetResizable(True)
wrap = QFrame(); wrap_l = QVBoxLayout(wrap); wrap_l.setContentsMargins(0,0,0,0); wrap_l.setSpacing(10)
wrap_l.addWidget(QLabel("Conteúdo da Minha Página")); wrap_l.addStretch(1)
scroll.setWidget(wrap); root.addWidget(scroll, 1)
def on_route(self, params: dict | None = None):
# Atualize conteúdo com base em params quando a rota for ativada
pass
def build(task_runner=None, theme_service=None) -> QWidget:
return MinhaPaginaPage(task_runner=task_runner, theme_service=theme_service)
- Registre no manifesto (opcional caso confie na auto‑descoberta)
// app/assets/pages_manifest.json
[
{ "route": "home", "label": "Início", "sidebar": true, "order": 0,
"factory": "app.pages.home_page:build" },
{ "route": "minha-pagina", "label": "Minha Página", "sidebar": true, "order": 50,
"factory": "app.pages.minha_pagina_page:build" }
]
- Execute e navegue: a página aparecerá na sidebar e breadcrumbs.
Subpáginas Manualmente (sem CLI)
- Rota e arquivo
- Para uma subpágina de
home, crieapp/pages/home_detalhes_page.pycomPAGE = { "route": "home/detalhes", ... }. - O breadcrumb mostrará
Home / Detalhesautomaticamente.
- SideBar e order
sidebar: truepara aparecer no menu;falsepara rotas internas.- Ajuste
orderpara ordenar a exibição na sidebar.
- Navegação programática
def _go(self, path: str):
win = self.window(); r = getattr(win, "router", None)
if r: r.go(path)
Widgets: Guia Prático (Cookbook)
Botões (Controls.Button / PrimaryButton)
from ui.widgets.buttons import Controls
btn = Controls.Button("Primário", size_preset="md")
btn.setProperty("variant", "primary") # outras: "chip", "ghost" (via QSS)
btn.clicked.connect(lambda: print("clicado"))
Tamanhos e variantes
size_preset:sm,md,lg,xl,char(um caractere)variant(QSS):primary,chip,ghost(e outras que seu QSS definir) Botão de comando (TaskRunner)
from ui.widgets.buttons import command_button
btn = command_button(
text="Sincronizar",
command_name="sync",
task_runner=my_runner,
payload={"force": True},
disable_while_running=True,
lock_after_click=False,
size_preset="md"
)
Confirmação + comando
from ui.widgets.buttons import confirm_command_button
btn = confirm_command_button(
text="Excluir",
confirm_msg="Tem certeza?",
command_name="delete_item",
task_runner=my_runner,
payload={"id": 123},
size_preset="sm"
)
Navegar por rota (route_button)
from ui.widgets.buttons import route_button
go = lambda: self.window().router.go("home/detalhes")
btn = route_button("Ir", go, size_preset="sm")
Toggle / CheckBox / TextInput / Select
t = Controls.Toggle(); t.setChecked(True)
ck = Controls.CheckBox("Aceito termos")
inp = Controls.TextInput("Digite aqui...")
sel = Controls.InputList(); sel.addItems(["A", "B", "C"])
Ícone / LinkLabel
icon = Controls.IconButton("⋯", tooltip="Mais")
link = Controls.LinkLabel("Abrir documentação"); link.clicked.connect(lambda: print("abrir"))
Popover de ajuda (hover)
from ui.widgets.buttons import attach_popover
attach_popover(icon, "Ação X", "Executa a ação X", "Ctrl+X")
Expandir/colapsar painel (ExpandMore)
from ui.widgets.buttons import Controls
panel = QFrame(); panel.setLayout(QVBoxLayout()); panel.layout().addWidget(QLabel("Detalhes"))
exp = Controls.ExpandMore(panel, text_collapsed="Ver mais", text_expanded="Ver menos")
layout.addWidget(exp)
Slider em modo progresso (não interativo)
s = Controls.Slider(); s.setRange(0,100); s.setValue(42); s.setMode("progress")
Scroll customizado
scroll = Controls.ScrollArea(); scroll.setWidget(wrap)
AsyncTaskButton (tarefas assíncronas)
from ui.widgets.async_button import AsyncTaskButton
ab = AsyncTaskButton(
"Executar",
task_runner=my_runner,
command_name="processar",
payload={"limit": 10},
block_input=True, # opcional: bloqueia com overlay
use_overlay=True,
overlay_message="Processando...",
progress_text="Processando…", progress_kind="info", progress_cancellable=False,
)
ab.succeeded.connect(lambda *_: print("ok"))
ab.failed.connect(lambda *_: print("falha"))
layout.addWidget(ab)
Toasts (simples, ação e progresso)
from ui.widgets.toast import show_toast, show_action_toast, ProgressToast
show_toast(self, "Salvo com sucesso", kind="ok")
show_action_toast(
self, "Exportação", "Arquivo pronto.", kind="ok",
actions=[{"label":"Abrir pasta","command":"abrir_pasta","payload":{}}], persist=True
)
pt = ProgressToast.start(self, "Sincronizando…", kind="info", cancellable=True)
pt.set_indeterminate(True)
pt.update(3, 10)
pt.finish(True, "Concluído")
LoadingOverlay manual
from ui.widgets.loading_overlay import LoadingOverlay
ov = LoadingOverlay(self, message="Carregando…", block_input=True)
ov.show(); ...; ov.hide()
Quick Open programático
from ui.dialogs.quick_open import QuickOpenDialog
dlg = QuickOpenDialog(self.window().router._pages.keys(), parent=self)
dlg.exec()
Personalização Visual & Chrome
- TitleBar: o ícone do app troca conforme o tema; ícones em
app/assets/icons/app/. - TopBar: breadcrumb e sino já integrados; badge reflete o Centro de Notificações.
- Sidebars:
OverlaySidePanel(navegação) eSettingsSidePanel(configurações) estilizáveis via QSS. - PushSidePanel (direita): painel do Centro de Notificações com redimensionamento por “grip”.
Centro de Notificações (avançado)
Interaja com o barramento para criar/atualizar/remover entradas:
from ui.widgets.toast import notification_bus
bus = notification_bus()
bus.addEntry.emit({
"id": "abc123", "type": "info", "title": "Processo iniciado",
"text": "Aguarde…", "persist": True, "expires_on_finish": True
})
bus.updateEntry.emit({"id": "abc123", "text": "50%"})
bus.finishEntry.emit("abc123")
bus.removeEntry.emit("abc123")
Temas: Dicas & Avançado
- Adicione variáveis em
*.jsonna chavevarse faça referência nobase.qssusando{{token}},{token}ou${token}. - Tokens derivados (ex.:
content_bg) são gerados automaticamente. - O ThemeService observa
base.qsse temas; alterações são aplicadas em tempo real. - Use o Editor de Temas (painel) para editar e salvar de forma segura (gravação atômica). Exemplo de uso de token no QSS
QPushButton[variant="chip"] {
background: {{surface}}; color: {{text}}; border: 1px solid {{box_border}};
}
Troubleshooting
- Página não aparece
- Verifique se o módulo está em
app/pagese exportaPAGEebuild(). - Cheque
pages_manifest.jsonou rodepython -m ui.cli manifest-update.
- Verifique se o módulo está em
- QSS não aplica
- Verifique
app/assets/qss/base.qss. O ThemeService emite sinais nas trocas; acompanhe logs.
- Verifique
- Toasts não aparecem
- Confirme se o app está rodando com
AppShellpadrão; toasts dependem do shell frameless.
- Confirme se o app está rodando com
Blueprint de Página (modelo recomendado)
Use este esqueleto como base para novas páginas, mantendo consistência visual e técnica.
from __future__ import annotations
from PySide6.QtWidgets import QWidget, QVBoxLayout, QScrollArea, QFrame, QLabel
# Metadados de rota (sidebar, ordem e rótulo)
PAGE = {
"route": "minha-pagina",
"label": "Minha Página",
"sidebar": True,
"order": 50,
}
class MinhaPaginaPage(QWidget):
def __init__(self, task_runner=None, theme_service=None):
super().__init__()
root = QVBoxLayout(self)
root.setContentsMargins(14, 14, 14, 14)
root.setSpacing(12)
# Área rolável para conteúdos longos
scroll = QScrollArea(self)
scroll.setWidgetResizable(True)
# Container real do conteúdo
wrap = QFrame()
wrap_l = QVBoxLayout(wrap)
wrap_l.setContentsMargins(0, 0, 0, 0)
wrap_l.setSpacing(10)
# Conteúdo inicial
wrap_l.addWidget(QLabel("Olá, mundo!"))
wrap_l.addStretch(1)
scroll.setWidget(wrap)
root.addWidget(scroll, 1)
# Hook de ciclo de vida: chamado quando a rota é ativada
def on_route(self, params: dict | None = None):
# Atualize conteúdo/estado a partir de params, se necessário
pass
def build(task_runner=None, theme_service=None) -> QWidget:
return MinhaPaginaPage(task_runner=task_runner, theme_service=theme_service)
Boas práticas para páginas
- Use
QScrollAreacomo container padrão para manter UX consistente. - Centralize estilos no
base.qss; evitesetStyleSheetdiretamente em widgets. - Utilize
on_route(params)para reagir a navegação em vez de lógicas no__init__. - Prefira nomes de rota em kebab-case (ex.:
relatorios-anuais).
Qualidade de Código (Linting & Style)
Ferramentas sugeridas
- Ruff (lint & fix):
pip install ruff•ruff check .•ruff check . --fix - Black (format):
pip install black•black . - isort (imports):
pip install isort•isort . - mypy (tipagem opcional):
pip install mypy•mypy ui appDicas de estilo - Tipagem: anote funções e atributos públicos; use
Optional[...]e| Noneonde apropriado. - Nomes: rotas em kebab-case; módulos/arquivos em snake_case; classes em PascalCase; variáveis/funções em snake_case.
- Sinais/slots: prefira nomes descritivos (
openNotificationsRequested,setUnreadCountRequested). - Páginas: exponha
PAGE+build(...); useon_route(params)para ciclo de vida. - QSS: declare variantes com
setProperty("variant", ...)e tamanhos comsetProperty("size", ...). - Imports: agrupe padrão→terceiros→locais; mantenha ordem alfabética (use
isort). - Formatação: 88 colunas (black), quebras legíveis, evite linhas muito longas. Sobre o arquivo pyproject.toml
- O que é: um arquivo único de configuração (na raiz do projeto) que centraliza as regras de ferramentas como Black, Ruff, isort e mypy.
- Onde fica:
pyproject.tomlna raiz deste repositório. - Por que usar: padroniza estilo e qualidade para todo o time, reduzindo ruído em PRs e tornando os comandos previsíveis.
- Como funciona: cada ferramenta lê suas opções diretamente do
pyproject.toml, então você não precisa passar flags nos comandos. Comandos do dia a dia (usam o pyproject.toml):
# Lint (apenas checar)
ruff check .
# Lint com correções automáticas
ruff check . --fix
# Formatar código (opiniado)
black .
# Organizar imports
isort .
# Checar tipos (opcional, gradual)
mypy ui app
Sugestões de fluxo
- Para iniciantes: rode
ruff check . --fixe depoisblack .; se houver import bagunçado, rodeisort .. - Para quem já domina: configure um pre-commit com
ruff,blackeisortou adicione esses passos no seu CI.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ui_fram_skk-0.1.1.tar.gz.
File metadata
- Download URL: ui_fram_skk-0.1.1.tar.gz
- Upload date:
- Size: 89.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9854fb98c5a22f00b9623334e24c4160ad88c7c251a426f7d27e44049c994538
|
|
| MD5 |
b87fff116dd4e39907d6b8b3c4704d58
|
|
| BLAKE2b-256 |
1e6216f0874c5c5f582d953da5f5cb3c087e2f202724d2d7563d5ec58e966460
|
File details
Details for the file ui_fram_skk-0.1.1-py3-none-any.whl.
File metadata
- Download URL: ui_fram_skk-0.1.1-py3-none-any.whl
- Upload date:
- Size: 91.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9608903191c92f620d3cf54978154dc88f5cd1570018f8354af27aaec5ea5c26
|
|
| MD5 |
9d1cc26a0a46fa1f7115fb28b3544e43
|
|
| BLAKE2b-256 |
4531e94e12d72734cd6c3cc3fec5ffbc308d0455a4071061f2e784dc10bbc464
|