Bloc de notas simple con Tk (tkinter)

Bloc de notas simple con Tk (tkinter)

Simple bloc de notas

He aquí un simple editor de archivos de texto escrito en Python con el módulo estándar tkinter. El programa está compuesto por una simple ventana, una barra de menús y un control tk.Text para mostrar el contenido de los archivos. Para permitir al usuario examinar los archivos se usa el módulo estándar tkinter.filedialog. Los archivos de texto se escriben y leen vía la clase estándar pathlib.Path. El código incluye anotaciones de tipos, por lo cual requiere Python 3.9 o superior. El resto está suficientemente comentado a continuación.

from typing import Optional
from tkinter import filedialog, messagebox
from pathlib import Path
import tkinter as tk


class Notepad(tk.Tk):

    def __init__(self) -> None:
        super().__init__()
        self.title("Bloc de notas")
        self.geometry("800x500")
        # Barra de menú.
        self.menubar = tk.Menu()
        self.file_menu = tk.Menu(tearoff=False)
        self.file_menu.add_command(label="Nuevo", command=self.new)
        self.file_menu.add_command(label="Abrir", command=self.open)
        self.file_menu.add_command(label="Guardar", command=self.save)
        self.file_menu.add_command(
            label="Guardar como...",
            command=self.save_as
        )
        self.menubar.add_cascade(menu=self.file_menu, label="Archivo")
        self.config(menu=self.menubar)
        # Control principal para editar el texto de un archivo.
        self.text = tk.Text()
        self.text.pack(expand=True, fill=tk.BOTH)
        # El atributo `current_file` contiene la ruta del archivo
        # que se está editando actualmente o `None` si aún no se
        # ha guardado el archivo.
        self.current_file: Optional[Path] = None
        # Tipos de archivos que aparecerán en los cuadros para
        # abrir o guardar un archivo.
        self.filetypes: tuple[tuple[str, str], ...] = (
            ("Archivo de texto", "*.txt"),
            ("Todos los archivos", "*.*")
        )
        # Reemplazar el mecanismo de cierre por defecto de Tk
        # para poder guardar el archivo antes de terminar
        # el programa.
        self.protocol("WM_DELETE_WINDOW", self.close)
    
    def close(self) -> None:
        if not self.can_continue():
            return
        # Destruir la ventana.
        self.destroy()
    
    def set_current_file(self, current_file: Path) -> None:
        self.current_file = current_file
        # Cuando cambia el archivo actual se actualiza
        # el título con el nombre del nuevo archivo.
        self.title(self.current_file.name + " - Bloc de notas")
    
    def can_continue(self) -> bool:
        # Si se hicieron modificaciones al texto, pedir la
        # confirmación al usuario.
        if self.text.edit_modified():
            result = messagebox.askyesnocancel(
                title="Cambios sin guardar",
                message="¿Desea guardar el archivo actual antes de continuar?"
            )
            cancel = result is None
            save_before = result is True
            # Si el usuario canceló el diálogo, no continuar.
            if cancel:
                return False
            # Si eligió guardar el archivo, guardar y continuar.
            elif save_before:
                self.save()
            return True
        # Si no hubo modificaciones, continuar sin guardar.
        return True
    
    def new(self) -> None:
        if not self.can_continue():
            return
        # Eliminar el texto anterior.
        self.text.delete("1.0", tk.END)
        self.current_file = None
        self.title("Bloc de notas")
    
    def open(self) -> None:
        filename = filedialog.askopenfilename(filetypes=self.filetypes)
        if not filename or not self.can_continue():
            return
        # Eliminar el texto anterior e insertar el contenido
        # del arhcivo seleccionado por el usuario.
        self.text.delete("1.0", tk.END)
        file = Path(filename)
        self.text.insert("1.0", file.read_text("utf8"))
        # Reiniciar el estado del texto cuando se abre un nuevo
        # archivo.
        self.text.edit_modified(False)
        self.set_current_file(file)
    
    def save_current_file(self) -> None:
        if self.current_file is None:
            return
        self.current_file.write_text(self.text.get("1.0", tk.END), "utf8")
    
    def save(self) -> None:
        if self.current_file is None:
            self.save_as()
            return
        self.save_current_file()

    def save_as(self) -> None:
        filename = filedialog.asksaveasfilename(
            defaultextension=".txt",
            filetypes=self.filetypes
        )
        # No hacer nada si el usuario canceló o cerró el
        # diálogo.
        if not filename:
            return
        self.set_current_file(Path(filename))
        self.save_current_file()


notepad = Notepad()
notepad.mainloop()

Curso online 👨‍💻

¡Ya lanzamos el curso oficial de Recursos Python en Udemy! Un curso moderno para aprender Python desde cero con programación orientada a objetos, SQL y tkinter en 2024.

Consultoría 💡

Ofrecemos servicios profesionales de desarrollo y capacitación en Python a personas y empresas. Consultanos por tu proyecto.

Deja una respuesta