Lección 15 · 15 de 20
Diálogos de archivo y de color · tkinter
Elija archivos, carpetas y colores con filedialog y colorchooser de tkinter, y sepa qué devuelve cada diálogo al aceptar o cancelar.

filedialog abre los diálogos de archivo del sistema. askopenfilename devuelve la ruta del archivo elegido como texto, o "" si se cancela; askopenfilenames (con ese final) deja elegir varios y devuelve una tupla de rutas. Ninguno abre el archivo: solo dicen cuál es.
asksaveasfilename pregunta dónde guardar: initialfile propone un nombre, defaultextension=".txt" añade la extensión si el usuario no la escribe y el sistema avisa si el archivo ya existe. askdirectory elige una carpeta. filetypes es una lista de pares (descripción, patrón) que filtra lo que se ve.
colorchooser.askcolor abre el selector de color y devuelve el color de dos formas: (rojo, verde, azul) y en hexadecimal, "#4f8cf7"; si se cancela, (None, None). Por eso run comprueba value[1] antes de pintar la muestra.
Como son modales, van detrás de botones. Cada fila tiene un botón y una etiqueta con el último resultado escrito con repr, para ver bien si es texto, tupla o vacío; al empezar enseñan los valores de ejemplo del diccionario shown. root.configure(padx=16, pady=12) deja un margen alrededor de la rejilla.
En DMarlon Studio están en la paleta el Diálogo Abrir y el Diálogo Guardar: no se ven en la ventana, se configuran en el inspector (título, filtro, extensión, carpeta inicial…) y el estudio genera el código que llama a askopenfilename y asksaveasfilename.
Código · main.py
import tkinter as tkfrom tkinter import ttk, filedialog, colorchooserroot = tk.Tk()root.title("File and color dialogs")root.geometry("560x240")root.configure(padx=16, pady=12) # space around the gridTYPES = [("Text files", "*.txt"), ("All files", "*.*")] # (description, pattern)# They return a path ("" if cancelled), a tuple of paths or a color.dialogs = { "Open…": lambda: filedialog.askopenfilename(title="Open", filetypes=TYPES), "Open several…": lambda: filedialog.askopenfilenames(filetypes=TYPES), "Save as…": lambda: filedialog.asksaveasfilename( defaultextension=".txt", initialfile="copy.txt", filetypes=TYPES), "Folder…": lambda: filedialog.askdirectory(title="Choose a folder"), "Color…": lambda: colorchooser.askcolor(color=swatch["bg"], title="Pick a color"),}shown = {"Open…": "/Users/ana/notes.txt", "Open several…": ("/Users/ana/a.txt", "/Users/ana/b.txt"), "Save as…": "/Users/ana/copy.txt", "Color…": ((79, 140, 247), "#4f8cf7")}results = {name: tk.StringVar(value=repr(shown.get(name, ""))) for name in dialogs}def run(name): value = dialogs[name]() results[name].set(repr(value)) if name == "Color…" and value[1]: # (None, None) when cancelled swatch.config(bg=value[1])for row, name in enumerate(dialogs): # a button and its result on each row ttk.Button(root, text=name, width=14, command=lambda n=name: run(n)).grid(row=row, column=0, pady=4) ttk.Label(root, textvariable=results[name]).grid(row=row, column=1, sticky="w", padx=10)swatch = tk.Label(root, bg="#4f8cf7", width=4)swatch.grid(row=4, column=2)root.mainloop()Para recordar
askopenfilenamedevuelve una ruta, o""si se cancela.asksaveasfilenameadmitedefaultextensioneinitialfile.askcolordevuelve((r, g, b), "#hex")o(None, None).filetypesfiltra con pares (descripción, patrón).
Ejercicio
Haga que «Open…» lea el archivo elegido (si no se canceló) y escriba en su etiqueta cuántas líneas tiene.