@katatsumuri está guapo, ...pero no es mejor crearse la APP con IA? ahora mientras comía le he pedido a GEMINI (con CLAUDE también se puede), que me haga una APP que lo haga y en 5 minutos me la ha hecho y compilada a .EXE
Dejo enlace de mi GOOGLE DRIVE por si a alguien le interesa ya en EXE
CONVERSOR MAYUS-MINUSLa APP tiene una ventana flotante que la puedes desplazar donde quieras, ...y seleccionando el texto que quieras y escogiendo en una opción u otra te hace la conversión
O si se apreta
CTRL + SHIFT + U convierte en MAYÚSCULAS (U de Uppercase)
CTRL + SHIFT + L convierte en MINÚSCULAR (L de Lowercase)

y se puede minimizar en la barra de tareas

Sinó aquí tenéis el código fuente en
PYTHON (Se debe tener PYTHON instalado para la compilación) . Tan sencillo como PEGAR todo este código en un archivo con extensión .PY (ejemplo teclado.py).
Previamente hay que tener instaladas las librerías
pip install pyperclip keyboard
pip install pystray pillow
pip install pyinstaller
import tkinter as tk
from tkinter import messagebox
import pyperclip
import keyboard
import time
import threading
from PIL import Image, ImageDraw
import pystray
class ConvertidorTextoApp:
def __init__(self, root):
self.root = root
# ELIMINAR LA BARRA DE TÍTULO DE WINDOWS (Quita pluma, blanco, etc.)
self.root.overrideredirect(True)
# Configuración de ventana flotante y siempre visible
self.root.attributes("-topmost", True)
self.root.geometry("240x40") # Reducimos un poco el alto ya que no hay barra blanca
self.root.configure(bg="#2d2d2d")
# Variables para permitir arrastrar la ventana sin barra de título
self.offset_x = 0
self.offset_y = 0
self.root.bind("<Button-1>", self.click_ventana)
self.root.bind("<B1-Motion>", self.arrastrar_ventana)
# Crear la interfaz gráfica compacta
self.crear_interfaz()
# Configurar los atajos de teclado globales (Hotkeys)
keyboard.add_hotkey('ctrl+shift+u', lambda: self.transformar_texto_desde_hotkey("MAYUS"))
keyboard.add_hotkey('ctrl+shift+l', lambda: self.transformar_texto_desde_hotkey("MINUS"))
# Control del icono de la bandeja
self.icono_tray = None
def crear_interfaz(self):
btn_estilo = {
"bg": "#404040",
"fg": "white",
"font": ("Arial", 9, "bold"),
"bd": 0,
"activebackground": "#555555",
"activeforeground": "white"
}
# Botón MAYÚSCULAS
btn_mayus = tk.Button(self.root, text="MAYÚS", command=lambda: self.transformar_texto_click("MAYUS"), **btn_estilo)
btn_mayus.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=2, pady=4)
# Botón minúsculas
btn_minus = tk.Button(self.root, text="minus", command=lambda: self.transformar_texto_click("MINUS"), **btn_estilo)
btn_minus.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=2, pady=4)
# Botón Minimizar (—) en gris
btn_min = tk.Button(self.root, text="—", command=self.minimizar_a_bandeja, bg="#3a3a3a", fg="white", bd=0, font=("Arial", 9, "bold"))
btn_min.pack(side=tk.LEFT, fill=tk.Y, padx=2, pady=4)
# Botón Cerrar (X) en rojo
btn_cerrar = tk.Button(self.root, text="X", command=self.cerrar_completo, bg="#cc2424", fg="white", bd=0, font=("Arial", 9, "bold"))
btn_cerrar.pack(side=tk.LEFT, fill=tk.Y, padx=2, pady=4)
# Funciones para permitir mover la ventana arrastrándola
def click_ventana(self, event):
self.offset_x = event.x
self.offset_y = event.y
def arrastrar_ventana(self, event):
x = self.root.winfo_x() + event.x - self.offset_x
y = self.root.winfo_y() + event.y - self.offset_y
self.root.geometry(f"+{x}+{y}")
def procesar_transformacion(self, modo):
pyperclip.copy("")
keyboard.send('ctrl+c')
time.sleep(0.15)
texto_seleccionado = pyperclip.paste()
if not texto_seleccionado.strip():
return False
if modo == "MAYUS":
texto_transformado = texto_seleccionado.upper()
elif modo == "MINUS":
texto_transformado = texto_seleccionado.lower()
pyperclip.copy(texto_transformado)
keyboard.send('ctrl+v')
time.sleep(0.15)
return True
def transformar_texto_click(self, modo):
self.root.withdraw()
time.sleep(0.15)
self.procesar_transformacion(modo)
self.root.deiconify()
def transformar_texto_desde_hotkey(self, modo):
threading.Thread(target=lambda: self.procesar_transformacion(modo), daemon=True).start()
def generar_icono_fluorescente(self):
image = Image.new('RGBA', (64, 64), (0, 0, 0, 0))
d = ImageDraw.Draw(image)
d.ellipse([4, 4, 60, 60], fill="#CCFF00")
d.text((16, 12), "Aa", fill="black", font_size=32, font_weight="bold")
return image
def minimizar_a_bandeja(self):
self.root.withdraw()
if self.icono_tray:
self.icono_tray.stop()
menu = pystray.Menu(
pystray.MenuItem('Mostrar App', self.restaurar_desde_bandeja, default=True),
pystray.MenuItem('Salir por completo', self.cerrar_completo)
)
self.icono_tray = pystray.Icon("convertidor_texto", self.generar_icono_fluorescente(), "Convertidor Aa", menu)
threading.Thread(target=self.icono_tray.run, daemon=True).start()
def restaurar_desde_bandeja(self, icon=None, item=None):
if self.icono_tray:
self.icono_tray.stop()
self.icono_tray = None
self.root.after(0, self.root.deiconify)
def cerrar_completo(self, icon=None, item=None):
if self.icono_tray:
self.icono_tray.stop()
keyboard.unhook_all()
self.root.destroy()
if __name__ == "__main__":
root = tk.Tk()
app = ConvertidorTextoApp(root)
root.mainloop()
Para compilarlo a EXE
pyinstaller --clean --noconsole --onefile --hidden-import=pystray._win32 nombrearchivo.py ![brindis [beer]](/images/smilies/nuevos2/brindando.gif)
PD: Ojo, se puede hacer con otro lenguaje de programación que consuma menos que con PYTHON eh.