Wiki »
Código e implementación » History » Version 4
« Previous -
Version 4/7
(diff) -
Next » -
Current version
Katalina Oviedo, 12/10/2024 04:19 AM
Error executing the thumbnail macro (Attachment AquaPi.png not found)
Índice¶
Código e implementación¶
Código interfaz gráficaCódigo interfaz gráfica
from PySide6.QtWidgets import QApplication, QLabel, QLineEdit, QPushButton, QVBoxLayout, QHBoxLayout, QWidget, QMessageBox, QStackedWidget, QSpacerItem, QSizePolicy, QLayout
from PySide6.QtGui import QPixmap, QIntValidator, QImage
from PySide6.QtCore import Qt, QUrl, QThread, Signal
from PySide6.QtWebEngineWidgets import QWebEngineView
import socket
import sys
import re
import cv2
import numpy as np
import time
from queue import Queue
class DataReceiver(QThread):
# Señales para actualizar los valores en la interfaz
data_received = Signal(float, float, int)
def __init__(self, host, port):
super().__init__()
self.host = host
self.port = port
self.running = True
def run(self):
try:
# Conexión al servidor
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((self.host, self.port))
while self.running:
# Recibir datos del servidor
data = s.recv(1024)
if not data:
break
# Decodificar y procesar los datos
decoded_data = data.decode('utf-8').strip()
try:
# Validar que los datos contengan exactamente dos comas (indicativo del formato esperado)
if decoded_data.count(',') == 2:
temp, ph, light = map(float, decoded_data.split(','))
self.data_received.emit(temp, ph, int(light))
else:
# Ignorar los datos que no tienen el formato correcto
print("")
except ValueError:
# Manejar cualquier error inesperado en el procesamiento
print("")
except Exception as e:
print("Error en la conexión:", e)
def stop(self):
self.running = False
self.quit()
class VideoReceiver(QThread):
frame_received = Signal(QImage)
def __init__(self, host, port):
super().__init__()
self.host = host
self.port = port
self.running = True
def run(self):
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(5) # Tiempo de espera de 5 segundos
s.connect((self.host, self.port))
while self.running:
# Leer el tamaño del frame (asegúrate de leer siempre 4 bytes)
size_data = b""
while len(size_data) < 4:
chunk = s.recv(4 - len(size_data))
if not chunk:
raise ConnectionError("Conexión cerrada por el servidor.")
size_data += chunk
frame_size = int.from_bytes(size_data, 'big')
# Recibir el frame completo
frame_data = b""
while len(frame_data) < frame_size:
chunk = s.recv(min(4096, frame_size - len(frame_data)))
if not chunk:
raise ConnectionError("Conexión cerrada por el servidor.")
frame_data += chunk
# Convertir a imagen
np_array = np.frombuffer(frame_data, dtype=np.uint8)
frame = cv2.imdecode(np_array, cv2.IMREAD_COLOR)
if frame is not None:
rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
height, width, channel = rgb_frame.shape
q_image = QImage(rgb_frame.data, width, height, QImage.Format_RGB888)
self.frame_received.emit(q_image)
except Exception as e:
print("Error en la recepción de video:", e)
finally:
s.close()
def stop(self):
self.running = False
self.wait()
class EnviarMensaje(QThread):
"""
Clase para enviar mensajes al servidor utilizando una conexión persistente
y una cola para optimizar el envío.
"""
def __init__(self, host, port):
super().__init__()
self.host = host
self.port = port
self.running = True
self.queue = Queue() # Cola para almacenar mensajes a enviar
self.socket = None # Conexión persistente con el servidor
def run(self):
"""
Mantiene una conexión persistente con el servidor y envía los mensajes
de la cola según se van añadiendo.
"""
try:
# Crear la conexión persistente con el servidor
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.settimeout(5)
self.socket.connect((self.host, self.port))
print(f"Conexión persistente establecida con {self.host}:{self.port}")
while self.running:
try:
# Obtener un mensaje de la cola (con timeout para evitar bloqueos infinitos)
mensaje = self.queue.get(timeout=1)
# Si se recibe None, indica que el hilo debe detenerse
if mensaje is None:
break
# Enviar el mensaje al servidor
self.socket.sendall(mensaje.encode('utf-8'))
print(f"Mensaje enviado: {mensaje}")
except Exception as e:
print(f"Ante la espera: {e}")
continue
except Exception as e:
print(f"Error al establecer conexión persistente: {e}")
finally:
# Asegurarse de cerrar la conexión al finalizar
if self.socket:
self.socket.close()
print("Conexión cerrada.")
def enviar_mensaje(self, mensaje):
"""
Coloca un mensaje en la cola para ser enviado.
"""
self.queue.put(mensaje)
def stop(self):
"""
Detiene el hilo limpiamente.
"""
self.running = False
self.queue.put(None) # Coloca un marcador para desbloquear el bucle
self.wait()
class AquaPiApp(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("AquaPi")
self.setFixedSize(346.32, 750)
self.rangos = {'temperatura': {'min': 1.0, 'max': 100.0}, 'ph': {'min': 1.0, 'max': 100.0}, 'luz': {'min': 1.0, 'max': 200.0}}
self.estado_parametros = {"temperatura": True, "ph": True, "luz": True} # True = dentro del rango
self.cargar_estilos()
self.stack = QStackedWidget(self)
self.setLayout(QVBoxLayout())
self.layout().setContentsMargins(0, 0, 0, 0)
self.layout().setSpacing(0)
self.layout().addWidget(self.stack)
# Crear widgets de monitoreo una sola vez
self.temp_label = QLabel("0°C")
self.ph_label = QLabel("0.0")
self.luz_label = QLabel("0%")
self.data_receiver = None
self.mostrar_pantalla_conexion()
def mostrar_pantalla_conexion(self):
layout = QVBoxLayout()
titulo = QLabel("AQUA PI")
titulo.setAlignment(Qt.AlignCenter)
titulo.setObjectName("titulo_aquapi")
entry_ip = QLineEdit()
entry_ip.setObjectName("texto_ruta")
entry_ip.setPlaceholderText("Ingrese la IP del servidor...")
entry_ip.returnPressed.connect(lambda: self.verificar_conexion(entry_ip.text()))
btn_conexion = QPushButton("Establecer Conexión")
btn_conexion.setObjectName("boton_conectar")
btn_conexion.clicked.connect(lambda: self.verificar_conexion(entry_ip.text()))
layout.addWidget(titulo)
layout.addWidget(entry_ip)
layout.addWidget(btn_conexion)
layout.setAlignment(Qt.AlignTop)
pantalla_conexion = QWidget()
pantalla_conexion.setLayout(layout)
self.stack.addWidget(pantalla_conexion)
self.stack.setCurrentWidget(pantalla_conexion)
def verificar_conexion(self, ip):
if not self.validar_ip(ip):
QMessageBox.critical(self, "Error de IP", "La IP ingresada no es válida.")
return
try:
# Iniciar recepción de datos
self.data_receiver = DataReceiver(ip, 5560)
self.data_receiver.data_received.connect(self.actualizar_datos) # Conexión correcta
self.data_receiver.start()
# Iniciar recepción de video
self.video_receiver = VideoReceiver(ip, 5561)
self.video_receiver.frame_received.connect(self.actualizar_video)
self.video_receiver.start()
# Crear la instancia de EnviarMensaje
self.enviar_mensajes = EnviarMensaje(ip, 5562)
self.enviar_mensajes.start()
# Mostrar la pantalla principal
self.mostrar_pantalla_monitoreo()
except Exception as e:
QMessageBox.critical(self, "Error de Conexión", f"No se pudo conectar al servidor: {e}")
def validar_ip(self, ip):
# Verificar si la IP ingresada tiene un formato válido
ip_regex = re.compile(r"^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$")
return ip_regex.match(ip) is not None
def mostrar_pantalla_monitoreo(self):
# Verificar si la pantalla de monitoreo ya existe
if hasattr(self, 'pantalla_monitoreo'):
self.stack.setCurrentWidget(self.pantalla_monitoreo)
return
# Crear layout para la pantalla de monitoreo
layout = QVBoxLayout()
layout.setContentsMargins(0, 0, 0, 0)
# Crear título
layout.addItem(QSpacerItem(20, 49, QSizePolicy.Minimum, QSizePolicy.Fixed))
titulo_label = QLabel("AQUAPI", self)
titulo_label.setAlignment(Qt.AlignCenter)
titulo_label.setObjectName("titulo_pantalla")
layout.addWidget(titulo_label)
layout.addItem(QSpacerItem(20, 49, QSizePolicy.Minimum, QSizePolicy.Fixed))
# Crear etiquetas para mostrar los valores (ya inicializadas)
self.temp_label = self.agregar_cuadro(layout, "assets/temperatura.png", "Temperatura", "0°C")
layout.addItem(QSpacerItem(20, 10))
self.ph_label = self.agregar_cuadro(layout, "assets/ph.png", "pH", "0.0")
layout.addItem(QSpacerItem(20, 10))
self.luz_label = self.agregar_cuadro(layout, "assets/luz.png", "Luz", "0%")
layout.addItem(QSpacerItem(20, 88))
# Crear barra inferior con botones
btn_transmision = self.img_button(texto="Transmision", ruta_imagen="assets/transmision.png", ancho=120, alto=140, callback=self.mostrar_pantalla_transmision)
btn_config = self.img_button(texto="Configuración", ruta_imagen="assets/config.png", ancho=120, alto=140, callback=self.mostrar_pantalla_config)
rectangulo_inferior = QWidget(self)
rectangulo_inferior.setObjectName("rectangulo")
rectangulo_inferior.setFixedHeight(79)
rectangulo_layout = QHBoxLayout(rectangulo_inferior)
rectangulo_layout.setContentsMargins(0, 0, 0, 0)
rectangulo_layout.addWidget(btn_transmision)
rectangulo_layout.addWidget(btn_config)
layout.addWidget(rectangulo_inferior)
# Guardar la pantalla de monitoreo en un atributo
self.pantalla_monitoreo = QWidget()
self.pantalla_monitoreo.setLayout(layout)
self.stack.addWidget(self.pantalla_monitoreo)
self.stack.setCurrentWidget(self.pantalla_monitoreo)
def actualizar_datos(self, temp, ph, light):
# Actualizar valores
self.temp_label.setText(f"{temp:.2f}°C")
self.ph_label.setText(f"{ph:.2f}")
self.luz_label.setText(f"{light}%")
# Validar y cambiar colores
self.validar_y_cambiar_color(self.temp_label, "temperatura", temp, self.parametro_temp_label)
# Validar y actualizar los colores para pH
self.validar_y_cambiar_color(self.ph_label, "ph", ph, self.parametro_ph_label)
# Validar y actualizar los colores para luz
self.validar_y_cambiar_color(self.luz_label, "luz", light, self.parametro_luz_label)
def validar_y_cambiar_color(self, label, parametro, valor, parametro_label):
"""
Valida el valor y cambia el color del frame y del label del parámetro si el valor no está en rango.
Envía un mensaje solo si el estado cambia (de dentro a fuera de rango o viceversa).
"""
valor_numerico = self.convertir_a_numero(valor)
dentro_de_rango = valor_numerico is not None and self.validar_valor(parametro, valor_numerico)
# Si el estado cambia, actualizar el estado y enviar el mensaje correspondiente
if self.estado_parametros[parametro] != dentro_de_rango and parametro == "ph":
self.estado_parametros[parametro] = dentro_de_rango
mensaje = "RELAY_ON" if not dentro_de_rango else "RELAY_OFF"
self.enviar_mensajes.enviar_mensaje(mensaje)
# Cambiar estilos visuales según el estado
if dentro_de_rango:
label.parentWidget().setStyleSheet(
"border: 8px solid; border-radius: 20px; background-color: #564D80; border-color: #564D80;"
)
parametro_label.setStyleSheet("color: white; font-size: 21px")
else:
label.parentWidget().setStyleSheet(
"border: 8px solid; border-radius: 20px; background-color: #984F4F; border-color: #984F4F;"
)
parametro_label.setStyleSheet("color: #EF5E5E; font-size: 21px")
def validar_valor(self, parametro, valor):
"""Valida si el valor está dentro del rango configurado para el parámetro."""
# Convertimos el parámetro a minúsculas para asegurar consistencia
parametro_lower = parametro.lower()
# Verificamos si el parámetro existe en los rangos
if parametro_lower not in self.rangos:
return False
# Accedemos al rango correspondiente
rango = self.rangos[parametro_lower] # Asegurando que se use la clave correcta
# Verificamos que el valor esté en el formato adecuado (convertirlo a float si es necesario)
try:
valor = float(valor) # Asegúrate de que 'valor' sea un número
except ValueError:
print(f"Valor no válido: {valor}")
return False
# Comprobamos si el valor está fuera del rango
if valor < rango['min'] or valor > rango['max']:
return False # Valor fuera de rango
else:
return True # Valor dentro del rango
def closeEvent(self, event):
if self.data_receiver:
self.data_receiver.stop()
if hasattr(self, 'video_receiver') and self.video_receiver:
self.video_receiver.stop()
super().closeEvent(event)
def actualizar_video(self, frame):
if self.video_label:
self.video_label.setPixmap(QPixmap.fromImage(frame))
def mostrar_pantalla_transmision(self):
layout = QVBoxLayout()
layout.setContentsMargins(0, 0, 0, 0)
layout.addItem(QSpacerItem(20, 49, QSizePolicy.Minimum, QSizePolicy.Fixed))
# Título de la pantalla
titulo_label = QLabel("AQUAPI", self)
titulo_label.setAlignment(Qt.AlignCenter)
titulo_label.setObjectName("titulo_pantalla")
layout.addWidget(titulo_label)
# Espaciado debajo del título
layout.addItem(QSpacerItem(20, 134, QSizePolicy.Minimum, QSizePolicy.Fixed))
self.video_label = QLabel(self)
self.video_label.setFixedSize(318, 242)
self.video_label.setStyleSheet("background-color: black;")
self.video_label.setAlignment(Qt.AlignCenter) # Asegura que el contenido del QLabel esté centrado
layout.addWidget(self.video_label, alignment=Qt.AlignCenter)
# Espaciado debajo del rectángulo
layout.addItem(QSpacerItem(0, 206, QSizePolicy.Minimum, QSizePolicy.Fixed))
# Botones de configuración y monitoreo
btn_config = self.img_button(texto="Configuración", ruta_imagen="assets/config.png", ancho=120, alto=140, callback=self.mostrar_pantalla_config)
btn_monitoreo = self.img_button(texto="Monitoreo", ruta_imagen="assets/monitoreo.png", ancho=120, alto=140, callback=self.mostrar_pantalla_monitoreo)
# Rectángulo inferior con los botones
rectangulo_inferior = QWidget(self)
rectangulo_inferior.setObjectName("rectangulo")
rectangulo_inferior.setFixedHeight(79)
rectangulo_layout_inferior = QHBoxLayout(rectangulo_inferior)
rectangulo_layout_inferior.setContentsMargins(0, 0, 0, 0)
rectangulo_layout_inferior.addWidget(btn_config)
rectangulo_layout_inferior.addItem(QSpacerItem(4, 0, QSizePolicy.Minimum, QSizePolicy.Fixed))
rectangulo_layout_inferior.addWidget(btn_monitoreo)
rectangulo_layout_inferior.addItem(QSpacerItem(14, 0, QSizePolicy.Minimum, QSizePolicy.Fixed))
layout.addWidget(rectangulo_inferior)
# Crear el widget final y agregarlo al stack
pantalla_transmision = QWidget()
pantalla_transmision.setLayout(layout)
self.stack.addWidget(pantalla_transmision)
# Mostrar la pantalla
self.stack.setCurrentWidget(pantalla_transmision)
def convertir_a_numero(self, valor):
"""
Convierte un valor a un número, eliminando caracteres extra como '°C' o '%'.
Si no es posible convertirlo, devuelve None.
"""
# Si el valor ya es un número, simplemente devolverlo
if isinstance(valor, (int, float)):
return valor
# Si es una cadena, intentar convertirla
try:
if isinstance(valor, str):
# Eliminar caracteres específicos como '°C' o '%'
if '°C' in valor:
valor = valor.replace('°C', '').strip()
if '%' in valor:
valor = valor.replace('%', '').strip()
# Convertir la cadena a un número
return float(valor)
except (ValueError, TypeError):
print("Error al convertir el valor:", valor)
return None
def img_button(self, texto, ruta_imagen, ancho=120, alto=140, callback=None):
boton = QPushButton()
boton.setObjectName("boton_accion")
boton.setFixedSize(ancho, alto)
layout = QVBoxLayout(boton)
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(0)
label_imagen = QLabel()
pixmap = QPixmap(ruta_imagen).scaled(50, 50, Qt.KeepAspectRatio, Qt.SmoothTransformation)
label_imagen.setPixmap(pixmap)
label_imagen.setAlignment(Qt.AlignCenter)
label_imagen.setMinimumSize(31, 31)
label_imagen.setStyleSheet("background-color: none")
layout.addWidget(label_imagen)
label_texto = QLabel(texto)
label_texto.setAlignment(Qt.AlignCenter)
label_texto.setStyleSheet("color: white; font-size: 14px; background-color: transparent; font-weight: normal")
layout.addWidget(label_texto)
layout.setSizeConstraint(QLayout.SetFixedSize)
if callback:
boton.clicked.connect(callback)
return boton
def input_frame(self, layout, ancho, alto, texto_bajo):
"""Crea un cuadro de entrada estilizado con un texto debajo y lo agrega al layout dado."""
cuadro_layout = QVBoxLayout()
# Crear el marco estilizado
frame = QWidget(self)
frame.setStyleSheet("border: 8px solid; border-radius: 20px; background-color: #564D80; border-color: #564D80;")
frame.setFixedSize(ancho, alto)
frame_layout = QVBoxLayout(frame)
frame_layout.setSpacing(0)
frame_layout.setContentsMargins(0, 0, 0, 0)
# Crear el campo de entrada
input_field = QLineEdit(frame)
# Función para validar manualmente el texto del input
def validar_entrada():
texto = input_field.text()
# Reemplazar comas por puntos
texto = texto.replace(",", ".")
# Validar si el texto es un número flotante válido
try:
valor = float(texto)
# Formatear el número para que muestre hasta dos decimales, pero no añada decimales innecesarios
if valor.is_integer():
input_field.setText(f"{int(valor)}") # Si es un número entero, mostrar sin decimales
else:
input_field.setText(f"{valor:.2f}") # Si tiene decimales, mostrar hasta dos decimales
except ValueError:
# Si no es un número válido, podemos vaciar el campo o dejarlo como está
input_field.setText("") # O, si prefieres, deja el valor sin cambiar
return
# Conectar el evento de perder el foco (cuando el usuario termina de editar)
input_field.editingFinished.connect(validar_entrada)
input_field.setAlignment(Qt.AlignCenter) # Centrar el texto ingresado
input_field.setObjectName("input_config") # Identificador para estilos
frame_layout.addWidget(input_field)
# Agregar el marco al diseño principal
cuadro_layout.addWidget(frame, alignment=Qt.AlignHCenter)
# Crear la etiqueta debajo del input
label_bajo = QLabel(texto_bajo, self)
label_bajo.setAlignment(Qt.AlignCenter)
label_bajo.setStyleSheet("color: white; font-size: 21px; background-color: none; margin-top: 5px")
cuadro_layout.addWidget(label_bajo, alignment=Qt.AlignHCenter)
# Agregar el cuadro al diseño recibido
layout.addLayout(cuadro_layout)
# Devolver el campo de entrada para seguimiento
return input_field
def mostrar_pantalla_config(self):
layout = QVBoxLayout()
layout.setContentsMargins(0, 0, 0, 0)
# Título
layout.addItem(QSpacerItem(20, 49, QSizePolicy.Minimum, QSizePolicy.Fixed))
titulo_label = QLabel("AQUAPI", self)
titulo_label.setAlignment(Qt.AlignCenter)
titulo_label.setObjectName("titulo_pantalla")
layout.addWidget(titulo_label)
layout.addItem(QSpacerItem(20, 30, QSizePolicy.Minimum, QSizePolicy.Fixed))
# Inputs
self.inputs = [] # Lista para almacenar referencias a los inputs
fila1_layout = QHBoxLayout()
label_temp = QLabel("Temperatura")
label_temp.setObjectName("parametro")
label_temp.setAlignment(Qt.AlignCenter)
layout.addWidget(label_temp)
input_min_temp = self.input_frame(fila1_layout, 116, 68, "Min")
input_max_temp = self.input_frame(fila1_layout, 116, 68, "Máx")
self.inputs.append(input_min_temp)
self.inputs.append(input_max_temp)
layout.addLayout(fila1_layout)
layout.addItem(QSpacerItem(0, 2, QSizePolicy.Minimum, QSizePolicy.Fixed))
fila2_layout = QHBoxLayout()
label_ph = QLabel("pH")
label_ph.setObjectName("parametro")
label_ph.setAlignment(Qt.AlignCenter)
layout.addWidget(label_ph)
input_min_ph = self.input_frame(fila2_layout, 116, 68, "Min")
input_max_ph = self.input_frame(fila2_layout, 116, 68, "Máx")
self.inputs.append(input_min_ph)
self.inputs.append(input_max_ph)
layout.addLayout(fila2_layout)
layout.addItem(QSpacerItem(0, 5, QSizePolicy.Minimum, QSizePolicy.Fixed))
fila3_layout = QHBoxLayout()
label_luz = QLabel("Luz")
label_luz.setObjectName("parametro")
label_luz.setAlignment(Qt.AlignCenter)
layout.addWidget(label_luz)
input_min_luz = self.input_frame(fila3_layout, 116, 68, "Min")
input_max_luz = self.input_frame(fila3_layout, 116, 68, "Máx")
self.inputs.append(input_min_luz)
self.inputs.append(input_max_luz)
layout.addLayout(fila3_layout)
layout.addItem(QSpacerItem(0, 20, QSizePolicy.Minimum, QSizePolicy.Fixed))
# Rellenar los valores en los inputs desde self.rangos
if self.rangos['temperatura']['min'] is not None:
self.inputs[0].setText(str(self.rangos['temperatura']['min']))
if self.rangos['temperatura']['max'] is not None:
self.inputs[1].setText(str(self.rangos['temperatura']['max']))
if self.rangos['ph']['min'] is not None:
self.inputs[2].setText(str(self.rangos['ph']['min']))
if self.rangos['ph']['max'] is not None:
self.inputs[3].setText(str(self.rangos['ph']['max']))
if self.rangos['luz']['min'] is not None:
self.inputs[4].setText(str(self.rangos['luz']['min']))
if self.rangos['luz']['max'] is not None:
self.inputs[5].setText(str(self.rangos['luz']['max']))
# Botón Confirmar
confirmar_btn = QPushButton("Confirmar", self)
confirmar_btn.setObjectName("confirmar_btn")
confirmar_btn.clicked.connect(self.enviar_datos) # Conectar el evento del botón
layout.addWidget(confirmar_btn, alignment=Qt.AlignCenter)
# Espaciador antes del rectángulo inferior
layout.addItem(QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Fixed))
# Rectángulo inferior con botones
pantalla_config = QWidget()
pantalla_config.setLayout(layout)
self.stack.addWidget(pantalla_config)
self.stack.setCurrentWidget(pantalla_config)
btn_monitoreo = self.img_button(
texto="Monitoreo",
ruta_imagen="assets/monitoreo.png",
ancho=120,
alto=140,
callback=self.mostrar_pantalla_monitoreo
)
btn_transmision = self.img_button(
texto="Transmision",
ruta_imagen="assets/transmision.png",
ancho=120,
alto=140,
callback=self.mostrar_pantalla_transmision
)
rectangulo_inferior = QWidget(self)
rectangulo_inferior.setObjectName("rectangulo")
rectangulo_inferior.setFixedHeight(79)
rectangulo_layout = QHBoxLayout(rectangulo_inferior)
rectangulo_layout.setContentsMargins(0, 0, 0, 0)
rectangulo_layout.addWidget(btn_monitoreo)
rectangulo_layout.addItem(QSpacerItem(10, 0, QSizePolicy.Minimum, QSizePolicy.Fixed))
rectangulo_layout.addWidget(btn_transmision)
rectangulo_layout.addItem(QSpacerItem(10, 0, QSizePolicy.Minimum, QSizePolicy.Fixed))
layout.addWidget(rectangulo_inferior)
def agregar_cuadro(self, layout, icono_path, parametro, valor):
# Crear el frame y asignarle un objectName único
frame = QWidget(self)
frame.setObjectName(parametro.lower()) # Asegurando que esté en minúsculas
frame.setStyleSheet(f"border: 8px solid; border-radius: 20px; background-color: #564D80; border-color: #564D80;")
frame_layout = QHBoxLayout(frame)
frame_layout.setSpacing(0)
frame_layout.setContentsMargins(0, 0, 0, 0)
frame.setFixedSize(262, 94)
icono_label = QLabel(frame)
pixmap = QPixmap(icono_path).scaled(57, 57, Qt.KeepAspectRatio, Qt.SmoothTransformation)
icono_label.setPixmap(pixmap)
icono_label.setObjectName("icono_label")
frame_layout.addWidget(icono_label)
text_layout = QVBoxLayout()
valor_label = QLabel(valor, frame)
valor_label.setObjectName("valor_label")
text_layout.addWidget(valor_label)
frame_layout.addLayout(text_layout)
text_layout.setSpacing(0)
text_layout.setContentsMargins(0, 0, 0, 0)
# Agregar el frame al layout
layout.addWidget(frame, alignment=Qt.AlignHCenter)
# Agregar el parámetro al layout
parametro_label = QLabel(parametro, self)
parametro_label.setAlignment(Qt.AlignCenter)
parametro_label.setStyleSheet("color: white ; font-size: 21px")
layout.addWidget(parametro_label)
# Actualizar la etiqueta correspondiente según el parámetro
if parametro.lower() == "temperatura":
self.temp_label = valor_label
self.parametro_temp_label = parametro_label
elif parametro.lower() == "ph":
self.ph_label = valor_label
self.parametro_ph_label = parametro_label
elif parametro.lower() == "luz":
self.luz_label = valor_label
self.parametro_luz_label = parametro_label
return valor_label
def cargar_estilos(self):
with open("styles.css", "r") as f:
css = f.read()
self.setStyleSheet(css)
def enviar_datos(self):
"""Recopila los datos ingresados en los inputs y los guarda."""
datos = [input_widget.text() for input_widget in self.inputs]
# Guardamos los rangos
self.rangos = {
'temperatura': {'min': float(datos[0]), 'max': float(datos[1])},
'ph': {'min': float(datos[2]), 'max': float(datos[3])},
'luz': {'min': float(datos[4]), 'max': float(datos[5])},
}
app = QApplication(sys.argv)
ventana = AquaPiApp()
ventana.show()
sys.exit(app.exec())
AquaPi.png (519 KB) Katalina Oviedo, 12/10/2024 04:03 AM