# Importando bibliotecas necesarias import numpy as np import pandas as pd import random import requests import tkinter as tk from tkinter import ttk, messagebox from ttkthemes import ThemedTk # Claves de API para Google Maps y OpenRouteService GOOGLE_MAPS_API_KEY = 'AIzaSyDTKSUqEs8U0tOX34N5yZPG1dij0FvJYWs' OPENROUTESERVICE_API_KEY = '5b3ce3597851110001cf6248012dba97c57a44ab83fe233ed58dbf87' # Cargando datos de geolocalización desde un archivo de Excel dataframe = pd.read_excel('Geolocalizacion.xlsx', sheet_name = "Matriz", index_col=0) matriz_distancia = dataframe.to_numpy() # Función para geolocalizar una dirección utilizando la API de Google Maps def geolocalizar(direccion): url = "https://maps.googleapis.com/maps/api/geocode/json" params = {"address": direccion, "key": GOOGLE_MAPS_API_KEY} response = requests.get(url, params = params) resp_json = response.json() lat = resp_json['results'][0]['geometry']['location']['lat'] lng = resp_json['results'][0]['geometry']['location']['lng'] return lng, lat # Función para calcular la distancia entre dos coordenadas utilizando la API de OpenRouteService def calcular_distancia(coordenada1, coordenada2): url = "https://api.openrouteservice.org/v2/directions/driving-car" params = {"start": f"{coordenada1[0]},{coordenada1[1]}", "end": f"{coordenada2[0]},{coordenada2[1]}", "api_key": OPENROUTESERVICE_API_KEY} response = requests.get(url, params = params) resp_json = response.json() distance = resp_json['features'][0]['properties']['segments'][0]['distance'] return distance # Función para agregar un nuevo cliente al archivo de Excel y calcular distancias def agregar_cliente(direccion, nombre_cliente): longitud, latitud = geolocalizar(direccion) coordenadas_df = pd.read_excel('Geolocalizacion.xlsx', sheet_name = "Coordenadas") matriz_df = pd.read_excel('Geolocalizacion.xlsx', sheet_name = "Matriz") nuevo_cliente = pd.DataFrame([{"Nombre": nombre_cliente, "Direccion": direccion, "Longitud": longitud, "Latitud": latitud}]) coordenadas_df = pd.concat([coordenadas_df, nuevo_cliente], ignore_index=True) distancias_hasta_nuevo = [] for index in range(matriz_df.shape[0]): row = coordenadas_df.iloc[index] distancia_hasta_nuevo = calcular_distancia((row["Longitud"], row["Latitud"]), (longitud, latitud)) distancias_hasta_nuevo.append(distancia_hasta_nuevo if distancia_hasta_nuevo else 0) matriz_df[nombre_cliente] = distancias_hasta_nuevo distancias_desde_nuevo = [nombre_cliente] for index in range(matriz_df.shape[0]): row = coordenadas_df.iloc[index] distancia_desde_nuevo = calcular_distancia((longitud, latitud), (row["Longitud"], row["Latitud"])) distancias_desde_nuevo.append(distancia_desde_nuevo if distancia_desde_nuevo else 0) distancias_desde_nuevo.append(0) matriz_df.loc[len(matriz_df)] = distancias_desde_nuevo with pd.ExcelWriter('Geolocalizacion.xlsx') as writer: coordenadas_df.to_excel(writer, sheet_name='Coordenadas', index=False) matriz_df.to_excel(writer, sheet_name='Matriz', index=False) return coordenadas_df, matriz_df # Función para crear una matriz con los clientes seleccionados def crear_matriz_seleccionados(clientes_seleccionados, matriz_completa_df): clientes_con_extremos = ['PARQUE INDUSTRIAL'] + [cliente[0] for cliente in clientes_seleccionados] + ['LA 12'] matriz_seleccionados = matriz_completa_df.loc[clientes_con_extremos, clientes_con_extremos] with pd.ExcelWriter('Geolocalizacion.xlsx', engine='openpyxl', mode='a') as writer: matriz_seleccionados.to_excel(writer, sheet_name="Matriz de Seleccionados") return matriz_seleccionados # Función para eliminar una hoja en particular de un archivo de Excel def eliminar_hoja_excel(filename, sheet_name): hojas = pd.read_excel(filename, sheet_name=None) if sheet_name in hojas: del hojas[sheet_name] with pd.ExcelWriter(filename, engine='xlsxwriter') as writer: for sheet, data in hojas.items(): data.to_excel(writer, sheet_name = sheet, index=False) # Clase para representar una estiba class Estiba: def __init__(self, largo, ancho): self.largo = largo self.ancho = ancho self.area = largo * ancho def rotar(self): return Estiba(self.ancho, self.largo) # Clase para representar una posición class Posicion: def __init__(self, x, y): self.x = x self.y = y # Clase para representar un vehículo y su organización de estibas class Vehiculo: def __init__(self, largo, ancho): self.largo = largo self.ancho = ancho self.estibas = [] self.matriz = [[0] * ancho for _ in range(largo)] def primera_posicion(self, estiba): for rotacion in [estiba, estiba.rotar()]: for i in range(self.largo - rotacion.largo + 1): for j in range(self.ancho - rotacion.ancho + 1): if all(self.matriz[i+k][j+l] == 0 for k in range(rotacion.largo) for l in range(rotacion.ancho)): return Posicion(j, i), rotacion return None, None def acomodar_estiba(self, posicion, estiba): for i in range(estiba.largo): for j in range(estiba.ancho): self.matriz[posicion.y+i][posicion.x+j] = 1 self.estibas.append((posicion, estiba)) # Clase para realizar el ruteo de clientes class Ruteo: def __init__(self, matriz_distancia, num_vehiculos, num_clientes, clientes_bodega, clientes_deposito): self.num_clientes = num_clientes self.BODEGA = 0 self.DEPOSITO = self.num_clientes + 1 self.clientes_bodega = clientes_bodega self.clientes_deposito = clientes_deposito self.matriz_distancia = matriz_distancia self.num_vehiculos = num_vehiculos def fitness(self, ruta, inicio, fin): distancia_total = 0 cliente_ant = inicio for cliente in ruta: distancia_total += self.matriz_distancia[cliente_ant][cliente] cliente_ant = cliente distancia_total += self.matriz_distancia[cliente_ant][fin] return distancia_total def calcular_costo_total(self, ruta, inicio, fin): longitud_total = len(ruta) clientes_por_vehiculo = longitud_total // self.num_vehiculos residuo = longitud_total % self.num_vehiculos inicio_ruta = 0 costo_total = 0 for i in range(self.num_vehiculos): fin_ruta = inicio_ruta + clientes_por_vehiculo if residuo > 0: fin_ruta += 1 residuo -= 1 segmento = ruta[inicio_ruta:fin_ruta] cliente_ant = inicio for cliente in segmento: distancia = self.matriz_distancia[cliente_ant][cliente] costo_total += distancia cliente_ant = cliente distancia_final = self.matriz_distancia[cliente_ant][fin] costo_total += distancia_final inicio_ruta = fin_ruta return costo_total def seleccion(self, populacion, scores): index = np.argsort(scores) return [populacion[i] for i in index[:2]] def cruce(self, padres): if len(padres[0]) < 3: return padres corte = random.randint(1, len(padres[0])-2) hijo1 = padres[0][:corte] + [i for i in padres[1] if i not in padres[0][:corte]] hijo2 = padres[1][:corte] + [i for i in padres[0] if i not in padres[1][:corte]] return hijo1, hijo2 def mutacion(self, ruta): if len(ruta) < 2: return ruta idx1, idx2 = random.sample(range(0, len(ruta)), 2) ruta[idx1], ruta[idx2] = ruta[idx2], ruta[idx1] return ruta def algoritmo_genetico(self, num_generaciones, num_poblacion, clientes, inicio, fin): poblacion = [random.sample(clientes, len(clientes)) for _ in range(num_poblacion)] for generacion in range(num_generaciones): scores = [self.calcular_costo_total(ruta, inicio, fin) for ruta in poblacion] padres = self.seleccion(poblacion, scores) hijos = self.cruce(padres) poblacion = padres + list(hijos) for i in range(len(poblacion)): if random.random() < 0.1: poblacion[i] = self.mutacion(poblacion[i]) scores_finales = [self.calcular_costo_total(ruta, inicio, fin) for ruta in poblacion] mejor_ruta = poblacion[np.argmin(scores_finales)] costo_total = min(scores_finales) return mejor_ruta, costo_total def segmentar_rutas(self, ruta): longitud_total = len(ruta) clientes_por_vehiculo = longitud_total // self.num_vehiculos residuo = longitud_total % self.num_vehiculos segmentos = [] inicio = 0 for _ in range(self.num_vehiculos): fin = inicio + clientes_por_vehiculo if residuo > 0: fin += 1 residuo -= 1 segmento = ruta[inicio:fin] segmentos.append(segmento) inicio = fin return segmentos def combinar_rutas(self, rutas_bodega, rutas_deposito): rutas_combinadas = [] for ruta_b, ruta_d in zip(rutas_bodega, rutas_deposito): if ruta_d: ruta_combinada = ruta_b + [self.DEPOSITO] + ruta_d else: ruta_combinada = ruta_b rutas_combinadas.append(ruta_combinada) return rutas_combinadas def resolver(self, clientes_seleccionados): ruta_bodega, costo_bodega = self.algoritmo_genetico(1000, 50, self.clientes_bodega, self.BODEGA, self.DEPOSITO) ruta_deposito, costo_deposito = self.algoritmo_genetico(1000, 50, self.clientes_deposito, self.DEPOSITO, self.BODEGA) segmentos_bodega = self.segmentar_rutas(ruta_bodega) segmentos_deposito = self.segmentar_rutas(ruta_deposito) rutas_finales = self.combinar_rutas(segmentos_bodega, segmentos_deposito) rutas_con_clientes = [] for ruta in rutas_finales: clientes_ruta = [(clientes_seleccionados[cliente-1][0], clientes_seleccionados[cliente-1][1]) for cliente in ruta if cliente != self.DEPOSITO] rutas_con_clientes.append(clientes_ruta) return rutas_finales, rutas_con_clientes # Clase principal de la aplicación para la interfaz gráfica class Interfaz: def __init__(self, root): self.root = root self.root.title("Selección de Clientes") self.area_total_orden = 0 self.clientes_bodega = [] self.clientes_deposito = [] self.clientes_seleccionados = [] self.contador_clientes = 0 title_frame = ttk.Frame(root, padding="10") title_frame.pack(pady=20) ttk.Label(title_frame, text="Selección de Clientes", font=("Arial", 18)).grid(row=0, column=0, sticky=tk.W) ttk.Label(title_frame, text="Por favor, elija a un cliente y proceda a configurar su pedido.", font=("Arial", 10)).grid(row=1, column=0, sticky=tk.W) input_frame = ttk.Frame(root, padding="10") input_frame.pack(pady=20) ttk.Label(input_frame, text="Selecciona un cliente:").grid(row=0, column=0, sticky=tk.W, padx=5, pady=5) self.seleccionado_var = tk.StringVar() self.desplegable_clientes = ttk.Combobox(input_frame, textvariable=self.seleccionado_var, width=25) self.desplegable_clientes.grid(row=0, column=1, padx=5, pady=5) self.cargar_lista_clientes() self.boton_seleccionar = ttk.Button(input_frame, text="Seleccionar", command=self.mostrar_emergente) self.boton_seleccionar.grid(row=1, column=0, padx=5, pady=5) self.boton_terminar = ttk.Button(input_frame, text="Terminar", command=self.terminar_seleccion) self.boton_terminar.grid(row=1, column=1, padx=5, pady=5) self.boton_agregar_cliente = ttk.Button(input_frame, text="Agregar Cliente", command=self.emergente_agregar_cliente) self.boton_agregar_cliente.grid(row=0, column=2, padx=10, pady=5) self.nkr_ii = {"ancho": 190, "largo": 480, "peso": 4000} self.nkr_i = {"ancho": 190, "largo": 400, "peso": 4000} self.nhr = {"ancho": 180, "largo": 320, "peso": 3000} self.estiba_grande = {"ancho": 100, "largo": 70} self.estiba_pequena = {"ancho": 90, "largo": 60} def cargar_lista_clientes(self): try: df = pd.read_excel('Geolocalizacion.xlsx', sheet_name = "Coordenadas") self.lista_clientes = [cliente for cliente in df['Nombre'].tolist() if cliente not in ["PARQUE INDUSTRIAL", "LA 12"]] self.desplegable_clientes["values"] = self.lista_clientes except (FileNotFoundError, pd.errors.EmptyDataError) as e: messagebox.showerror("Error", f"Se produjo un inconveniente al cargar la lista de clientes: {e}") def mostrar_emergente(self): cliente_seleccionado = self.seleccionado_var.get() if cliente_seleccionado: cliente_existente = next((cliente for cliente in self.clientes_seleccionados if cliente[0] == cliente_seleccionado), None) if cliente_existente: respuesta = messagebox.askyesno("Modificar Orden", "Este cliente ya ha sido seleccionado. ¿Le gustaría realizar alguna modificación en la orden?") if not respuesta: return emergente = tk.Toplevel(self.root) emergente.title("Datos del pedido") emergente.geometry("400x350") ttk.Label(emergente, text=f"Cliente: {cliente_seleccionado}", font=("Arial", 12)).pack(pady=15) form_frame = ttk.Frame(emergente) form_frame.pack(pady=10) ttk.Label(form_frame, text="Cantidad de estibas grandes:").grid(row=0, column=0, padx=10, pady=10) entrada_estibas_grandes = ttk.Entry(form_frame) entrada_estibas_grandes.grid(row=0, column=1, padx=10, pady=10) ttk.Label(form_frame, text="Cantidad de estibas pequeñas:").grid(row=1, column=0, padx=10, pady=10) entrada_estibas_pequena = ttk.Entry(form_frame) entrada_estibas_pequena.grid(row=1, column=1, padx=10, pady=10) ttk.Label(form_frame, text="Peso del pedido (Kg):").grid(row=2, column=0, padx=10, pady=10) peso_entrada = ttk.Entry(form_frame) peso_entrada.grid(row=2, column=1, padx=10, pady=10) deposito_frame = ttk.Frame(emergente) deposito_frame.pack(pady=15) deposito_var = tk.StringVar() deposito_var.set(None) ttk.Label(deposito_frame, text="¿Se debe pasar por LA 12?").pack(side=tk.LEFT, padx=10) ttk.Radiobutton(deposito_frame, text="Sí", variable=deposito_var, value="yes").pack(side=tk.LEFT, padx=5) ttk.Radiobutton(deposito_frame, text="No", variable=deposito_var, value="no").pack(side=tk.LEFT, padx=5) if cliente_existente: entrada_estibas_grandes.insert(0, cliente_existente[1]['Estibas grandes']) entrada_estibas_pequena.insert(0, cliente_existente[1]['Estibas pequeñas']) peso_entrada.insert(0, cliente_existente[1].get('Peso', 0)) boton_submit = ttk.Button(emergente, text="Enviar", command=lambda: self.emergente_submit(emergente, cliente_seleccionado, entrada_estibas_pequena.get(), entrada_estibas_grandes.get(), peso_entrada.get(), deposito_var.get())) boton_submit.pack(pady=10) def emergente_submit(self, emergente, cliente, estibas_pequena, estibas_grandes, peso, deposito): if deposito not in ["yes", "no"]: eleccion_deposito = messagebox.askyesno("Depósito", "No seleccionaste si hay que pasar por el depósito. ¿Deseas pasar por el depósito?") if eleccion_deposito: deposito = "yes" else: deposito = "no" emergente.destroy() cliente_existente_index = next((i for i, cliente in enumerate(self.clientes_seleccionados) if cliente[0] == cliente), None) if cliente_existente_index is not None: numero_cliente = self.clientes_seleccionados[cliente_existente_index][2] if numero_cliente in self.clientes_deposito: self.clientes_deposito.remove(numero_cliente) if numero_cliente in self.clientes_bodega: self.clientes_bodega.remove(numero_cliente) del self.clientes_seleccionados[cliente_existente_index] else: self.contador_clientes += 1 numero_cliente = self.contador_clientes try: estibas_pequena = int(estibas_pequena) if estibas_pequena else 0 estibas_grandes = int(estibas_grandes) if estibas_grandes else 0 peso = int(peso) if peso else 0 except ValueError: tk.messagebox.showerror("Error", "Por favor, introduzca un número válido para las estibas y/o peso.") return if deposito == "yes": self.clientes_deposito.append(numero_cliente) else: self.clientes_bodega.append(numero_cliente) detalles_orden = {"Estibas grandes": estibas_grandes, "Estibas pequeñas": estibas_pequena, "Peso": peso} self.clientes_seleccionados.append((cliente, detalles_orden, numero_cliente)) self.seleccionado_var.set("") self.area_total_orden += (self.estiba_pequena["ancho"] * self.estiba_pequena["largo"] * estibas_pequena) + (self.estiba_grande["ancho"] * self.estiba_grande["largo"] * estibas_grandes) peso_total = sum(cliente[1]['Peso'] for cliente in self.clientes_seleccionados) if peso_total <= self.nkr_ii["peso"]: self.num_vehiculos_peso = 1 elif peso_total <= self.nkr_ii["peso"] + self.nkr_i["peso"]: self.num_vehiculos_peso = 2 elif peso_total <= self.nkr_ii["peso"] + self.nkr_i["peso"] + self.nhr["peso"]: self.num_vehiculos_peso = 3 else: tk.messagebox.showerror("Error", "El peso de los pedidos supera la capacidad de carga de los camiones.") self.clientes_seleccionados.pop() if cliente_existente_index is None: self.contador_clientes -= 1 return if self.area_total_orden <= self.nkr_ii["ancho"] * self.nkr_ii["largo"]: self.num_vehiculos_area = 1 elif self.area_total_orden <= self.nkr_i["ancho"] * self.nkr_i["largo"] + self.nkr_ii["ancho"] * self.nkr_ii["largo"]: self.num_vehiculos_area = 2 else: self.num_vehiculos_area = 3 self.num_vehiculos = max(self.num_vehiculos_peso, self.num_vehiculos_area) def terminar_seleccion(self): dataframe = pd.read_excel('Geolocalizacion.xlsx', sheet_name = 'Matriz', index_col=0) matriz_seleccionados_df = crear_matriz_seleccionados(self.clientes_seleccionados, dataframe) matriz_seleccionados = matriz_seleccionados_df.to_numpy() ruteo = Ruteo(matriz_seleccionados, self.num_vehiculos, len(self.clientes_seleccionados), self.clientes_bodega, self.clientes_deposito) rutas_finales, rutas_con_clientes = ruteo.resolver(self.clientes_seleccionados) eliminar_hoja_excel('Geolocalizacion.xlsx', 'Matriz de Seleccionados') self.mostrar_resultados(rutas_finales, rutas_con_clientes) def mostrar_resultados(self, rutas_finales, rutas_con_clientes): self.mostrar_ruteo(rutas_finales, rutas_con_clientes) estibas_por_vehiculo = [] for ruta in rutas_con_clientes: estibas = [] for cliente in ruta: for i in range(cliente[1]['Estibas pequeñas']): estibas.append(Estiba(self.estiba_pequena["largo"], self.estiba_pequena["ancho"])) for i in range(cliente[1]['Estibas grandes']): estibas.append(Estiba(self.estiba_grande["largo"], self.estiba_grande["ancho"])) estibas_por_vehiculo.append(estibas) vehiculos = [{'largo': self.nkr_ii["largo"], 'ancho': self.nkr_ii["ancho"]}, {'largo': self.nkr_i["largo"], 'ancho': self.nkr_i["ancho"]}, {'largo': self.nhr["largo"], 'ancho': self.nhr["ancho"]}] resultados = resolver_dosL(vehiculos, estibas_por_vehiculo, rutas_con_clientes, self.estiba_grande, self.estiba_pequena) self.mostrar_organizacion_estibas(resultados, rutas_con_clientes) def mostrar_ruteo(self, rutas_finales, rutas_con_clientes): ventana_ruteo = tk.Toplevel(self.root) ventana_ruteo.title("Resultados del Ruteo") texto_resultados = "Rutas finales:\n" for i, ruta in enumerate(rutas_finales): ruta_con_nombres = [] for cliente in ruta: if cliente == len(self.clientes_seleccionados) + 1: ruta_con_nombres.append("LA 12") else: ruta_con_nombres.append(self.clientes_seleccionados[cliente-1][0]) vehiculo = "NKR II" if i == 0 else "NKR I" if i == 1 else "NHR" texto_resultados += f"Vehículo {vehiculo}: {', '.join(ruta_con_nombres)}\n" #texto_resultados += "\nRutas con detalles de los clientes:\n" #for i, ruta in enumerate(rutas_con_clientes): #ruta_con_nombres = [f"{cliente[0]} ({cliente[1]['Estibas grandes']} grandes, {cliente[1]['Estibas pequeñas']} pequeñas)" for cliente in ruta] #vehiculo = "NKR II" if i == 0 else "NKR I" if i == 1 else "NHR" #texto_resultados += f"Vehículo {vehiculo}: {', '.join(ruta_con_nombres)}\n" etiqueta_resultados = tk.Label(ventana_ruteo, text=texto_resultados) etiqueta_resultados.pack(pady=10) ventana_ruteo.protocol("WM_DELETE_WINDOW", lambda: self.cerrar_ventanas(ventana_ruteo)) def mostrar_organizacion_estibas(self, estibas_por_vehiculo, rutas_con_clientes): ventana_estibas = tk.Toplevel(self.root) ventana_estibas.title("Organización de Estibas") todas_estibas_colocadas = [] for i, vehiculo in enumerate(estibas_por_vehiculo): estibas_colocadas = [] for posicion, estiba in vehiculo.estibas: estibas_colocadas.append(f"Estiba de: {estiba.ancho}x{estiba.largo} en la posición: ({posicion.x}, {posicion.y})") todas_estibas_colocadas.append(f"Vehículo {i+1}: {estibas_colocadas}" ) texto_widget = tk.Text(ventana_estibas, wrap=tk.WORD, width=60, height=20) texto_widget.pack(pady=10) texto_widget.insert(tk.END, "\n".join(todas_estibas_colocadas)) scrollbar = ttk.Scrollbar(ventana_estibas, command=texto_widget.yview) scrollbar.pack(side=tk.RIGHT, fill=tk.Y) texto_widget.config(yscrollcommand=scrollbar.set) ventana_estibas.protocol("WM_DELETE_WINDOW", lambda: self.cerrar_ventanas(ventana_estibas)) def cerrar_ventanas(self, *windows): for ventana in windows: ventana.destroy() self.root.destroy() def emergente_agregar_cliente(self): emergente = tk.Toplevel(self.root) emergente.title("Añadir Cliente") emergente.geometry("600x300") form_frame = ttk.Frame(emergente, padding="20 20 20 10") form_frame.pack(pady=30, padx=20) ttk.Label(form_frame, text="Nombre del cliente:").grid(row=0, column=0, padx=10, pady=10) nombre_entry = ttk.Entry(form_frame, width=40) nombre_entry.grid(row=0, column=1, columnspan=2, padx=10, pady=10) ttk.Label(form_frame, text="Dirección del cliente:").grid(row=1, column=0, padx=10, pady=10) direccion_entry = ttk.Entry(form_frame, width=30) direccion_entry.grid(row=1, column=1, padx=10, pady=10) ciudades = ["Bucaramanga", "Floridablanca", "Giron", "Piedecuesta"] ciudad_combobox = ttk.Combobox(form_frame, values=ciudades, width=12, state="readonly") ciudad_combobox.grid(row=1, column=2, padx=10, pady=10) placeholder_text = "Ejemplo: Carrera 12 #42-37" direccion_entry.insert(0, placeholder_text) direccion_entry.bind("", lambda event: self.focus_in(event, direccion_entry, placeholder_text)) direccion_entry.bind("", lambda event: self.focus_out(event, direccion_entry, placeholder_text)) submit_frame = ttk.Frame(emergente, padding="20") submit_frame.pack(pady=20) ttk.Button(submit_frame, text="Enviar", command=lambda: self.submit_cliente(nombre_entry.get().upper(), f"{direccion_entry.get()}, {ciudad_combobox.get()}", emergente)).pack() def focus_in(self, event, entry, placeholder): if entry.get() == placeholder: entry.delete(0, tk.END) def focus_out(self, event, entry, placeholder): if not entry.get(): entry.insert(0, placeholder) def submit_cliente(self, nombre, direccion_completa, emergente): if not nombre or not direccion_completa: messagebox.showerror("Error", "Ambos campos son obligatorios.") return coordenadas_df = pd.read_excel('Geolocalizacion.xlsx', sheet_name="Coordenadas") if nombre in coordenadas_df['Nombre'].values: messagebox.showerror("Error", f"El cliente con el nombre '{nombre}' ya existe.") return if direccion_completa in coordenadas_df['Direccion'].values: messagebox.showerror("Error", f"La dirección '{direccion_completa}' ya está registrada.") return coordenadas_df, matriz_df = agregar_cliente(direccion_completa, nombre) self.cargar_lista_clientes() emergente.destroy() messagebox.showinfo("Éxito", "Cliente añadido con éxito.") # Función auxiliar para ordenar estibas def ordenar_estibas(estibas): estibas.sort(key=lambda load: estiba.area, reverse=True) return estibas # Función principal para resolver la organización de las estibas en los vehículos def resolver_dosL(vehiculos, estibas_por_vehiculo, rutas_con_clientes, estiba_grande, estiba_pequena): resultados = [] for vehicle, estibas, ruta in zip(vehiculos, estibas_por_vehiculo, rutas_con_clientes): vehiculo = Vehiculo(vehicle['largo'], vehicle['ancho']) ruta_reves = ruta[::-1] for cliente in ruta_reves: for i in range(cliente[1]['Estibas pequeñas']): estiba = Estiba(estiba_pequena["largo"], estiba_pequena["ancho"]) posicion, carga_optima = vehiculo.primera_posicion(estiba) if posicion: vehiculo.acomodar_estiba(posicion, carga_optima) for i in range(cliente[1]['Estibas grandes']): estiba = Estiba(estiba_grande["largo"], estiba_grande["ancho"]) posicion, carga_optima = vehiculo.primera_posicion(estiba) if posicion: vehiculo.acomodar_estiba(posicion, carga_optima) resultados.append(vehiculo) return resultados if __name__ == "__main__": root = ThemedTk(theme="radiance") root.geometry("500x300") app = Interfaz(root) root.mainloop()