messagebox.showerror(
"Ruta requerida",
"Debes indicar una ruta completa. Usa 'Examinar...' o escribe una ruta absoluta (por ejemplo, C:\\carpeta\\archivo.txt)."
)
return
# Evitar que se indique una carpeta como archivo
if os.path.isdir(archivo_path):
messagebox.showerror(
"Error",
"La ruta indicada es una carpeta. Especifica un archivo (por ejemplo, datos.txt)."
)
return
# Verificar/crear carpeta
try:
dir_path = os.path.dirname(os.path.abspath(archivo_path))
except (OSError, ValueError):
messagebox.showerror("Error", "La ruta del archivo destino no es válida")
return
if dir_path and not os.path.exists(dir_path):
crear = messagebox.askyesno(
"Crear carpeta",
f"La carpeta no existe:\n{dir_path}\n\n¿Deseas crearla?"
)
if crear:
try:
os.makedirs(dir_path, exist_ok=True)
except OSError as e:
messagebox.showerror("Error", f"No se pudo crear la carpeta:\n{e}")
return
else:
return
self._mostrar_progreso_gen()
header = (
"ID|Nombre|Email|Edad|Salario|FechaNacimiento|Activo|Codigo|Telefono|Puntuacion|Categoria|Comentarios\n"
)
with open(archivo_path, 'w', encoding='utf-8') as f:
f.write(header)
tamano_actual = len(header.encode('utf-8'))
rid = 1
while tamano_actual < tamano_objetivo_bytes:
linea = self._generar_registro_aleatorio(rid)
f.write(linea)
tamano_actual += len(linea.encode('utf-8'))
rid += 1
if rid % 1000 == 0:
# Actualización periódica del progreso para no saturar la UI
try:
if self.root.winfo_exists():
progreso = min(100, (tamano_actual / tamano_objetivo_bytes) * 100)
self.progress['value'] = progreso
self.estado_label.config(
text=f"Registros... {rid:,} registros ({progreso:.1f}%)")
self.root.update()
except tk.TclError:
break
tamano_real_bytes = os.path.getsize(archivo_path)
tamano_real_mb = tamano_real_bytes / (1024 * 1024)
try:
if self.root.winfo_exists():
self.progress['value'] = 100
self.estado_label.config(text="¡Archivo generado exitosamente!", fg='#4CAF50')
self.root.update()
except tk.TclError:
pass
abrir = messagebox.askyesno(
"Archivo Generado",
"Archivo creado exitosamente:\n\n"
f"Ruta: {archivo_path}\n"
f"Tamaño objetivo: {tamano_objetivo_mb:,.1f} MB\n"
f"Tamaño real: {tamano_real_mb:.1f} MB\n"
f"Registros generados: {rid-1:,}\n\n"
"¿Deseas abrir la carpeta donde se guardó el archivo?"
)
if abrir:
try:
destino = os.path.abspath(archivo_path)
# Abrir Explorer seleccionando el archivo generado
subprocess.run(['explorer', '/select,', destino], check=True)
except (OSError, subprocess.CalledProcessError) as e:
print(f"No se pudo abrir Explorer: {e}")
try:
if self.root.winfo_exists():
self.root.after(3000, self._ocultar_progreso_gen)
except tk.TclError:
pass
except (IOError, OSError, ValueError) as e:
messagebox.showerror("❌ Error", f"Error al generar el archivo:\n{str(e)}")
try:
if self.root.winfo_exists():
self.estado_label.config(text="❌ Error en la generación", fg='red')
self.root.after(2000, self._ocultar_progreso_gen)
except tk.TclError:
pass
# ------------------------------
# Lógica: División de archivo
# ------------------------------
def _dividir_archivo(self):
"""Divide un archivo en múltiples partes respetando líneas completas.
Reglas y comportamiento:
- El tamaño máximo de cada parte se define en "Tamaño por parte (MB)".
- No corta líneas: si una línea no cabe en la parte actual y ésta ya tiene
contenido, se inicia una nueva parte y se escribe allí la línea completa.
- Los nombres de salida se forman como: <base>_NN<ext> (NN con 2 dígitos).
Manejo de errores:
- Valida ruta de origen, tamaño de parte y tamaño > 0 del archivo.
- Muestra mensajes de error/aviso según corresponda.
"""
try:
src = self.split_source_file.get()
if not src or not os.path.isfile(src):
messagebox.showerror("Error", "Selecciona un archivo origen válido")
return
part_size_mb = self.split_size_mb.get()
if part_size_mb <= 0:
messagebox.showerror("Error", "El tamaño por parte debe ser mayor a 0")
return
part_size_bytes = int(part_size_mb * 1024 * 1024)
total_bytes = os.path.getsize(src)
if total_bytes == 0:
messagebox.showwarning("Aviso", "El archivo está vacío")
return
self._mostrar_progreso_split()
base, ext = os.path.splitext(src)
part_idx = 1
bytes_procesados = 0
bytes_en_parte = 0
out = None
def abrir_nueva_parte(idx: int):
nonlocal out, bytes_en_parte
if out:
out.close()
nombre = f"{base}_{idx:02d}{ext}"
out = open(nombre, 'wb') # escritura binaria
bytes_en_parte = 0
abrir_nueva_parte(part_idx)
line_count = 0
with open(src, 'rb') as fin: # lectura binaria
for linea in fin:
lb = len(linea)
# Si excede y ya escribimos algo, nueva parte
if bytes_en_parte > 0 and bytes_en_parte + lb > part_size_bytes:
part_idx += 1
abrir_nueva_parte(part_idx)
# Escribimos la línea completa
out.write(linea)
bytes_en_parte += lb
bytes_procesados += lb
line_count += 1
# Actualizar progreso cada 1000 líneas
if line_count % 1000 == 0:
try:
if self.root.winfo_exists():
progreso = min(100, (bytes_procesados / total_bytes) * 100)
self.split_progress['value'] = progreso
self.split_estado_label.config(
text=f"Procesando... {line_count:,} líneas ({progreso:.1f}%)")
self.root.update()
except tk.TclError:
break
if out:
out.close()
try:
if self.root.winfo_exists():
self.split_progress['value'] = 100
self.split_estado_label.config(text="¡Archivo dividido exitosamente!", fg='#4CAF50')
self.root.update()
except tk.TclError:
pass
abrir = messagebox.askyesno(
"División completada",
"El archivo se dividió correctamente en partes con sufijos _01, _02, ...\n\n"
f"Origen: {src}\n"
f"Tamaño por parte: {part_size_mb:.1f} MB\n\n"
"¿Deseas abrir la carpeta del archivo origen?"
)
if abrir:
try:
# Si existe la primera parte, seleccionarla; si no, abrir carpeta del origen
base, ext = os.path.splitext(src)
primera_parte = f"{base}_{1:02d}{ext}"
if os.path.exists(primera_parte):
subprocess.run(['explorer', '/select,', os.path.abspath(primera_parte)], check=True)
else:
carpeta = os.path.dirname(src)
subprocess.run(['explorer', carpeta], check=True)
except (OSError, subprocess.CalledProcessError) as e:
print(f"No se pudo abrir Explorer: {e}")
try:
if self.root.winfo_exists():
self.root.after(3000, self._ocultar_progreso_split)
except tk.TclError:
pass
except (IOError, OSError, ValueError) as e:
messagebox.showerror("❌ Error", f"Error al dividir el archivo:\n{str(e)}")
try:
if self.root.winfo_exists():
self.split_estado_label.config(text="❌ Error en la división", fg='red')
self.root.after(2000, self._ocultar_progreso_split)
except tk.TclError:
pass
def main():
"""Punto de entrada de la aplicación.
Crea la ventana raíz, instancia la clase de la UI, centra la ventana y
arranca el loop principal de Tkinter.
"""
root = tk.Tk()
GeneradorArchivo(root)
# Centrar ventana
root.update_idletasks()
width = root.winfo_width()
height = root.winfo_height()
x = (root.winfo_screenwidth() // 2) - (width // 2)
y = (root.winfo_screenheight() // 2) - (height // 2)
root.geometry(f"{width}x{height}+{x}+{y}")
root.mainloop()
if __name__ == "__main__":
main()