Módulo 16 - Projeto final: seu gerenciador de tarefas

Polindo o programa: os toques finais

12 min de leitura · por Cesar Gargiulo, revisado pela equipe ValorFinal e GuardiaSec · Atualizado em 01/07/2026

O que você vai aprender

  • Aplicar strip em todos os inputs para eliminar espaços acidentais.
  • Recusar tarefa vazia e exigir confirmação s/n antes de remover.
  • Adicionar o cabeçalho de abertura e a contagem de pendentes na saída.
  • Conferir o seu programa contra o código final completo e comentado.

Os cinco toques que fazem diferença

O gerenciador funciona, e isso já vale comemoração. Mas experimente usá-lo por uma semana e os incômodos aparecem: um espaço digitado sem querer antes da tarefa fica gravado para sempre, um Enter apressado cria uma tarefa fantasma vazia, um 4 digitado por engano apaga uma tarefa sem perguntar nada. Nenhum desses é um defeito de lógica; todos são falta de acabamento. Programas bons se diferenciam dos medianos exatamente aqui, nos detalhes que respeitam o usuário distraído.

ToqueO problema que resolveA ferramenta
Cabeçalho de aberturaO programa começa seco, sem identidadeprint com moldura de sinais de igual
strip nos inputsEspaços acidentais entram nos dadosinput(...).strip()
Recusa de tarefa vaziaEnter apressado cria tarefa fantasmaif texto == "" com return
Confirmação s/n na remoçãoUm número errado apaga sem dóinput de confirmação + lower()
Pendentes na despedidaA saída não informa nada útilfor com contador sobre a lista

Os cinco toques da aula: cada um resolve um incômodo real de uso, com ferramentas que o curso já ensinou.

Repare na coluna de ferramentas: strip e lower são do módulo 12, o if com return você usou a aula inteira passada, e o contador com for é o módulo 9 puro. Polimento não é assunto avançado; é atitude. A pergunta que o separa do resto é simples: o que acontece se o usuário fizer a coisa errada aqui? Faça essa pergunta em cada input do seu programa e o acabamento se escreve sozinho.

Aplicando o polimento, mudança por mudança

Vamos às mudanças cirúrgicas. Em adicionar_tarefa, o strip entra grudado no input e o if barra o texto vazio antes do append. Toda entrada de dados do programa ganha o mesmo strip: as duas leituras de número (em concluir e remover) e a leitura da opção do menu.

def adicionar_tarefa(tarefas):
    texto = input("Digite a tarefa: ").strip()
    if texto == "":
        print("Tarefa vazia não vale. Nada foi adicionado.")
        return
    tarefas.append({"texto": texto, "concluida": False})
    print(f"Tarefa adicionada: {texto}")

adicionar_tarefa polida: strip limpa as pontas e o if recusa o vazio antes que ele vire tarefa fantasma.

Em remover_tarefa, a confirmação entra depois da validação do número e antes do pop. O lower() aceita S maiúsculo e minúsculo como sim, e qualquer outra resposta cancela: na dúvida, não apague. Essa assimetria é proposital e é o padrão de todo software cuidadoso: a ação destrutiva exige um sim explícito; o cancelamento aceita qualquer coisa.

    # dentro de remover_tarefa, após validar o número:
    confirma = input(f"Remover '{tarefas[numero - 1]['texto']}'? (s/n): ").strip().lower()
    if confirma != "s":
        print("Remoção cancelada.")
        return
    removida = tarefas.pop(numero - 1)
    print(f"Removida: {removida['texto']}")

A confirmação mostra o texto da tarefa na pergunta, para a pessoa saber exatamente o que está prestes a apagar.

Faltam as pontas do programa: o cabeçalho na abertura, logo após o carregar_tarefas, e a despedida com a contagem de pendentes na opção 5. A contagem é um for com contador que soma 1 para cada tarefa cujo concluida é False. Sair do programa informando o que ficou por fazer transforma a despedida em lembrete útil.

O código final completo

Chegou o momento de gala do curso: o programa inteiro, do primeiro comentário à última linha do loop, com todos os toques aplicados. Use este código como gabarito de conferência do seu, não como fonte de cópia; se você construiu aula a aula, a diferença entre o seu arquivo e este deve ser mínima. Leia com orgulho: semanas atrás, você não sabia o que era uma variável.

# gerenciador.py - Gerenciador de tarefas
# Projeto final do curso de Python Básico do ValorFinal.
# Menu no terminal, tarefas em lista de dicionários, gravação em tarefas.txt.

ARQUIVO = "tarefas.txt"

def mostrar_menu():
    # Mostra as opções a cada volta do loop principal.
    print("\n=== MINHAS TAREFAS ===")
    print("1. Adicionar tarefa")
    print("2. Listar tarefas")
    print("3. Concluir tarefa")
    print("4. Remover tarefa")
    print("5. Sair")

def adicionar_tarefa(tarefas):
    # Pede o texto, recusa vazio e anexa o dicionário na lista.
    texto = input("Digite a tarefa: ").strip()
    if texto == "":
        print("Tarefa vazia não vale. Nada foi adicionado.")
        return
    tarefas.append({"texto": texto, "concluida": False})
    print(f"Tarefa adicionada: {texto}")

def listar_tarefas(tarefas):
    # Numera a partir do 1 e marca [x] concluída, [ ] pendente.
    if len(tarefas) == 0:
        print("Nenhuma tarefa na lista. Que tal adicionar a primeira?")
        return
    for i, tarefa in enumerate(tarefas, start=1):
        if tarefa["concluida"]:
            marca = "[x]"
        else:
            marca = "[ ]"
        print(f"{i}. {marca} {tarefa['texto']}")

def concluir_tarefa(tarefas):
    # Escolhe por número, com conversão protegida e intervalo validado.
    if len(tarefas) == 0:
        print("Não há tarefas para concluir.")
        return
    listar_tarefas(tarefas)
    resposta = input("Número da tarefa concluída: ").strip()
    try:
        numero = int(resposta)
    except ValueError:
        print("Digite apenas o número da tarefa.")
        return
    if numero < 1 or numero > len(tarefas):
        print("Não existe tarefa com esse número.")
        return
    tarefas[numero - 1]["concluida"] = True
    print("Tarefa marcada como concluída. Bom trabalho!")

def remover_tarefa(tarefas):
    # Mesmo escudo de validação, mais a confirmação s/n antes do pop.
    if len(tarefas) == 0:
        print("Não há tarefas para remover.")
        return
    listar_tarefas(tarefas)
    resposta = input("Número da tarefa a remover: ").strip()
    try:
        numero = int(resposta)
    except ValueError:
        print("Digite apenas o número da tarefa.")
        return
    if numero < 1 or numero > len(tarefas):
        print("Não existe tarefa com esse número.")
        return
    confirma = input(f"Remover '{tarefas[numero - 1]['texto']}'? (s/n): ").strip().lower()
    if confirma != "s":
        print("Remoção cancelada.")
        return
    removida = tarefas.pop(numero - 1)
    print(f"Removida: {removida['texto']}")

def salvar_tarefas(tarefas):
    # Reescreve o arquivo inteiro: uma linha por tarefa, status;texto.
    with open(ARQUIVO, "w", encoding="utf-8") as arquivo:
        for tarefa in tarefas:
            if tarefa["concluida"]:
                status = "1"
            else:
                status = "0"
            arquivo.write(status + ";" + tarefa["texto"] + "\n")

def carregar_tarefas():
    # Reconstrói a lista a partir do arquivo; sem arquivo, começa vazia.
    tarefas = []
    try:
        with open(ARQUIVO, "r", encoding="utf-8") as arquivo:
            for linha in arquivo:
                linha = linha.strip()
                if linha == "":
                    continue
                status, texto = linha.split(";", 1)
                tarefas.append({"texto": texto, "concluida": status == "1"})
    except FileNotFoundError:
        pass
    return tarefas

# Programa principal: carrega, apresenta e entra no loop do menu.
tarefas = carregar_tarefas()
print("=" * 26)
print("   MINHAS TAREFAS 1.0")
print("=" * 26)

while True:
    mostrar_menu()
    opcao = input("Escolha uma opção: ").strip()
    if opcao == "1":
        adicionar_tarefa(tarefas)
        salvar_tarefas(tarefas)
    elif opcao == "2":
        listar_tarefas(tarefas)
    elif opcao == "3":
        concluir_tarefa(tarefas)
        salvar_tarefas(tarefas)
    elif opcao == "4":
        remover_tarefa(tarefas)
        salvar_tarefas(tarefas)
    elif opcao == "5":
        pendentes = 0
        for tarefa in tarefas:
            if not tarefa["concluida"]:
                pendentes += 1
        print(f"Até logo! Tarefas pendentes: {pendentes}")
        break
    else:
        print("Opção inválida. Digite um número de 1 a 5.")

O gerenciador de tarefas completo: 7 funções, validação em todas as entradas, persistência em arquivo e os toques de acabamento.

Rode a bateria final de testes: adicione uma tarefa com espaços nas pontas e confira que ficou limpa; tente adicionar uma vazia e veja a recusa; remova uma tarefa respondendo n na confirmação e confira que ela sobreviveu; saia com pendências e veja a contagem na despedida; feche e reabra o programa e confira que tudo voltou do arquivo. Passou em tudo? Então respire fundo: você escreveu, sozinho e entendendo cada linha, um programa completo em Python. A próxima aula fecha o curso olhando para trás e para frente.

Teste rápido

Na confirmação da remoção, por que qualquer resposta diferente de s cancela, em vez de exigir um n explícito para cancelar?

Perguntas frequentes

Meu código ficou um pouco diferente do gabarito. Está errado?
Não necessariamente. Mensagens com outras palavras, nomes de variáveis locais diferentes e espaçamentos distintos são variação normal. O que precisa bater é o comportamento: os cinco toques funcionando, as validações segurando entrada errada e o arquivo indo e voltando. Teste contra o roteiro da aula; passou, está certo.
Por que o strip também entrou na leitura da opção do menu?
Pelo mesmo motivo dos outros inputs: um espaço acidental transformaria "1 " numa opção diferente de "1", e o usuário cairia no aviso de opção inválida sem entender o porquê. Normalizar toda entrada assim que ela chega é um hábito barato que elimina uma família inteira de bugs de eu jurava que tinha digitado certo.
O que faz exatamente o print("=" * 26) do cabeçalho?
Multiplicar uma string por um número repete a string: "=" * 26 produz uma linha com 26 sinais de igual. É um truque do módulo 12 para desenhar molduras sem digitar caractere por caractere. Mude o número e a moldura estica ou encolhe junto.
Por que contar as pendentes com um for em vez de contar as concluídas?
Porque a informação útil na despedida é o que ainda falta, não o que já foi. O for percorre a lista e soma 1 sempre que concluida é False, usando o not para inverter o teste. Contar as concluídas daria um número verdadeiro, mas responderia a pergunta errada.
O programa está pronto. Devo memorizar este código?
Não. Memorize o desenho, não as linhas: um loop de menu, funções por ação, validação em toda entrada, gravação após toda mudança. Esses quatro princípios reconstroem este programa e servem de planta para dezenas de outros. Código se consulta; estrutura se carrega na cabeça.
Posso publicar esse projeto no meu GitHub?
Pode e é uma boa ideia, com a honestidade de sempre: descreva como projeto de estudo do curso de Python Básico. Melhor ainda se você adicionar alguma evolução sua depois, como as ideias da próxima aula; a diferença entre o gabarito e a sua versão é o que mostra a sua digital.

Fontes

Seu progresso fica salvo neste aparelho. Assinantes sincronizam entre os aparelhos.