Skip to main content

Framework de UI desktop (PySide6) com janela frameless, roteamento e temas

Project description

App Icon

Read this in English


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


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), routeChanged e hook on_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.qss e 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 Router
  • router.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/*.json Exemplo
{
  "vars": { "bg": "#1a1a1a", "text": "#ffffff", "accent": "#4285f4" }
}

ThemeService

  • Anima troca de tema (com ou sem animação).
  • Observa base.qss e 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) (com update(...)/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(), hook on_route() e estrutura com QScrollArea pronta 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.py criado 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.py com usos práticos (botões, AsyncTaskButton, toasts) para consulta.
  • Uso (bash):
python -m ui.cli examples
  • Resultado: rota examples adicionada ao manifesto para navegação rápida.

manifest-update

  • O que faz: reescreve app/assets/pages_manifest.json com base na auto‑descoberta de app.pages.* (lendo PAGE e build()).
  • 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

  1. Duplique app/assets/themes/Dark.json.
  2. Renomeie (ex.: MyTheme.json) e ajuste cores.
  3. 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; evite setStyleSheet direto.
  • Separe UI de lógica de negócio (via task_runner).
  • Padronize rotas (kebab‑case) e labels claros.
  • Use theme_service para 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
    • Instale dependências: pip install -r requirements.txt
    • Rode o app: python -m ui.core.main

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.py com:
      • PAGE = { route, label, sidebar, order }
      • build(task_runner=None, theme_service=None)
      • Estrutura com QScrollArea pronta 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.
  • 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.
  • examples
    • Cria examples_widgets_page.py com usos práticos de botões, AsyncTaskButton e toasts.
    • Exemplo: python -m ui.cli examples
    • Manifesto: adiciona rota examples.
  • manifest-update
    • Reescreve completamente app/assets/pages_manifest.json com base na auto‑descoberta de app.pages.*.
    • Útil após mover/renomear páginas manualmente.
    • Exemplo: python -m ui.cli manifest-update
  • clean-pages
    • Remove páginas pré‑programadas de exemplo e cria uma Home mínima.
    • Exemplo: python -m ui.cli clean-pages (ou --rebuild-manifest para 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 de app/pages.

Criando Páginas Manualmente (sem CLI)

  1. Crie um arquivo em app/pages/minha_pagina_page.py com:
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)
  1. 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" }
]
  1. Execute e navegue: a página aparecerá na sidebar e breadcrumbs.

Subpáginas Manualmente (sem CLI)

  1. Rota e arquivo
  • Para uma subpágina de home, crie app/pages/home_detalhes_page.py com PAGE = { "route": "home/detalhes", ... }.
  • O breadcrumb mostrará Home / Detalhes automaticamente.
  1. SideBar e order
  • sidebar: true para aparecer no menu; false para rotas internas.
  • Ajuste order para ordenar a exibição na sidebar.
  1. 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) e SettingsSidePanel (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 *.json na chave vars e faça referência no base.qss usando {{token}}, {token} ou ${token}.
  • Tokens derivados (ex.: content_bg) são gerados automaticamente.
  • O ThemeService observa base.qss e 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/pages e exporta PAGE e build().
    • Cheque pages_manifest.json ou rode python -m ui.cli manifest-update.
  • QSS não aplica
    • Verifique app/assets/qss/base.qss. O ThemeService emite sinais nas trocas; acompanhe logs.
  • Toasts não aparecem
    • Confirme se o app está rodando com AppShell padrão; toasts dependem do shell frameless.

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 QScrollArea como container padrão para manter UX consistente.
  • Centralize estilos no base.qss; evite setStyleSheet diretamente 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 ruffruff check .ruff check . --fix
  • Black (format): pip install blackblack .
  • isort (imports): pip install isortisort .
  • mypy (tipagem opcional): pip install mypymypy ui app Dicas de estilo
  • Tipagem: anote funções e atributos públicos; use Optional[...] e | None onde 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(...); use on_route(params) para ciclo de vida.
  • QSS: declare variantes com setProperty("variant", ...) e tamanhos com setProperty("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.toml na 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 . --fix e depois black .; se houver import bagunçado, rode isort ..
  • Para quem já domina: configure um pre-commit com ruff, black e isort ou adicione esses passos no seu CI.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ui_fram_skk-0.1.1.tar.gz (89.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ui_fram_skk-0.1.1-py3-none-any.whl (91.4 kB view details)

Uploaded Python 3

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

Hashes for ui_fram_skk-0.1.1.tar.gz
Algorithm Hash digest
SHA256 9854fb98c5a22f00b9623334e24c4160ad88c7c251a426f7d27e44049c994538
MD5 b87fff116dd4e39907d6b8b3c4704d58
BLAKE2b-256 1e6216f0874c5c5f582d953da5f5cb3c087e2f202724d2d7563d5ec58e966460

See more details on using hashes here.

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

Hashes for ui_fram_skk-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 9608903191c92f620d3cf54978154dc88f5cd1570018f8354af27aaec5ea5c26
MD5 9d1cc26a0a46fa1f7115fb28b3544e43
BLAKE2b-256 4531e94e12d72734cd6c3cc3fec5ffbc308d0455a4071061f2e784dc10bbc464

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page