Código e implementación » History » Version 5
Katalina Oviedo, 12/10/2024 04:20 AM
| 1 | 2 | Katalina Oviedo | {{thumbnail(AquaPi.png, size=300, title=Logo)}} |
|---|---|---|---|
| 2 | 2 | Katalina Oviedo | |
| 3 | 2 | Katalina Oviedo | h2. Índice |
| 4 | 2 | Katalina Oviedo | |
| 5 | 2 | Katalina Oviedo | * [[Introducción]] |
| 6 | 2 | Katalina Oviedo | * [[Objetivos]] |
| 7 | 2 | Katalina Oviedo | * [[Organización y planificación]] |
| 8 | 3 | Katalina Oviedo | * [[Interfaz]] |
| 9 | 2 | Katalina Oviedo | |
| 10 | 2 | Katalina Oviedo | --- |
| 11 | 2 | Katalina Oviedo | |
| 12 | 1 | Bruno Amestica | h1. Código e implementación |
| 13 | 4 | Katalina Oviedo | |
| 14 | 4 | Katalina Oviedo | {{collapse(Código interfaz gráfica) |
| 15 | 4 | Katalina Oviedo | <pre><code class="ruby"> |
| 16 | 4 | Katalina Oviedo | from PySide6.QtWidgets import QApplication, QLabel, QLineEdit, QPushButton, QVBoxLayout, QHBoxLayout, QWidget, QMessageBox, QStackedWidget, QSpacerItem, QSizePolicy, QLayout |
| 17 | 4 | Katalina Oviedo | from PySide6.QtGui import QPixmap, QIntValidator, QImage |
| 18 | 4 | Katalina Oviedo | from PySide6.QtCore import Qt, QUrl, QThread, Signal |
| 19 | 4 | Katalina Oviedo | from PySide6.QtWebEngineWidgets import QWebEngineView |
| 20 | 4 | Katalina Oviedo | import socket |
| 21 | 4 | Katalina Oviedo | import sys |
| 22 | 4 | Katalina Oviedo | import re |
| 23 | 4 | Katalina Oviedo | import cv2 |
| 24 | 4 | Katalina Oviedo | import numpy as np |
| 25 | 4 | Katalina Oviedo | import time |
| 26 | 4 | Katalina Oviedo | from queue import Queue |
| 27 | 4 | Katalina Oviedo | |
| 28 | 4 | Katalina Oviedo | class DataReceiver(QThread): |
| 29 | 4 | Katalina Oviedo | # Señales para actualizar los valores en la interfaz |
| 30 | 4 | Katalina Oviedo | data_received = Signal(float, float, int) |
| 31 | 4 | Katalina Oviedo | |
| 32 | 4 | Katalina Oviedo | def __init__(self, host, port): |
| 33 | 4 | Katalina Oviedo | super().__init__() |
| 34 | 4 | Katalina Oviedo | self.host = host |
| 35 | 4 | Katalina Oviedo | self.port = port |
| 36 | 4 | Katalina Oviedo | self.running = True |
| 37 | 4 | Katalina Oviedo | |
| 38 | 4 | Katalina Oviedo | def run(self): |
| 39 | 4 | Katalina Oviedo | try: |
| 40 | 4 | Katalina Oviedo | # Conexión al servidor |
| 41 | 4 | Katalina Oviedo | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
| 42 | 4 | Katalina Oviedo | s.connect((self.host, self.port)) |
| 43 | 4 | Katalina Oviedo | |
| 44 | 4 | Katalina Oviedo | while self.running: |
| 45 | 4 | Katalina Oviedo | # Recibir datos del servidor |
| 46 | 4 | Katalina Oviedo | data = s.recv(1024) |
| 47 | 4 | Katalina Oviedo | if not data: |
| 48 | 4 | Katalina Oviedo | break |
| 49 | 4 | Katalina Oviedo | |
| 50 | 4 | Katalina Oviedo | # Decodificar y procesar los datos |
| 51 | 4 | Katalina Oviedo | decoded_data = data.decode('utf-8').strip() |
| 52 | 4 | Katalina Oviedo | try: |
| 53 | 4 | Katalina Oviedo | # Validar que los datos contengan exactamente dos comas (indicativo del formato esperado) |
| 54 | 4 | Katalina Oviedo | if decoded_data.count(',') == 2: |
| 55 | 4 | Katalina Oviedo | temp, ph, light = map(float, decoded_data.split(',')) |
| 56 | 4 | Katalina Oviedo | self.data_received.emit(temp, ph, int(light)) |
| 57 | 4 | Katalina Oviedo | |
| 58 | 4 | Katalina Oviedo | else: |
| 59 | 4 | Katalina Oviedo | # Ignorar los datos que no tienen el formato correcto |
| 60 | 4 | Katalina Oviedo | print("") |
| 61 | 4 | Katalina Oviedo | except ValueError: |
| 62 | 4 | Katalina Oviedo | # Manejar cualquier error inesperado en el procesamiento |
| 63 | 4 | Katalina Oviedo | print("") |
| 64 | 4 | Katalina Oviedo | except Exception as e: |
| 65 | 4 | Katalina Oviedo | print("Error en la conexión:", e) |
| 66 | 4 | Katalina Oviedo | |
| 67 | 4 | Katalina Oviedo | def stop(self): |
| 68 | 4 | Katalina Oviedo | self.running = False |
| 69 | 4 | Katalina Oviedo | self.quit() |
| 70 | 4 | Katalina Oviedo | |
| 71 | 4 | Katalina Oviedo | class VideoReceiver(QThread): |
| 72 | 4 | Katalina Oviedo | frame_received = Signal(QImage) |
| 73 | 4 | Katalina Oviedo | |
| 74 | 4 | Katalina Oviedo | def __init__(self, host, port): |
| 75 | 4 | Katalina Oviedo | super().__init__() |
| 76 | 4 | Katalina Oviedo | self.host = host |
| 77 | 4 | Katalina Oviedo | self.port = port |
| 78 | 4 | Katalina Oviedo | self.running = True |
| 79 | 4 | Katalina Oviedo | |
| 80 | 4 | Katalina Oviedo | def run(self): |
| 81 | 4 | Katalina Oviedo | try: |
| 82 | 4 | Katalina Oviedo | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
| 83 | 4 | Katalina Oviedo | s.settimeout(5) # Tiempo de espera de 5 segundos |
| 84 | 4 | Katalina Oviedo | s.connect((self.host, self.port)) |
| 85 | 4 | Katalina Oviedo | |
| 86 | 4 | Katalina Oviedo | while self.running: |
| 87 | 4 | Katalina Oviedo | # Leer el tamaño del frame (asegúrate de leer siempre 4 bytes) |
| 88 | 4 | Katalina Oviedo | size_data = b"" |
| 89 | 4 | Katalina Oviedo | while len(size_data) < 4: |
| 90 | 4 | Katalina Oviedo | chunk = s.recv(4 - len(size_data)) |
| 91 | 4 | Katalina Oviedo | if not chunk: |
| 92 | 4 | Katalina Oviedo | raise ConnectionError("Conexión cerrada por el servidor.") |
| 93 | 4 | Katalina Oviedo | size_data += chunk |
| 94 | 4 | Katalina Oviedo | |
| 95 | 4 | Katalina Oviedo | frame_size = int.from_bytes(size_data, 'big') |
| 96 | 4 | Katalina Oviedo | |
| 97 | 4 | Katalina Oviedo | # Recibir el frame completo |
| 98 | 4 | Katalina Oviedo | frame_data = b"" |
| 99 | 4 | Katalina Oviedo | while len(frame_data) < frame_size: |
| 100 | 4 | Katalina Oviedo | chunk = s.recv(min(4096, frame_size - len(frame_data))) |
| 101 | 4 | Katalina Oviedo | if not chunk: |
| 102 | 4 | Katalina Oviedo | raise ConnectionError("Conexión cerrada por el servidor.") |
| 103 | 4 | Katalina Oviedo | frame_data += chunk |
| 104 | 4 | Katalina Oviedo | |
| 105 | 4 | Katalina Oviedo | # Convertir a imagen |
| 106 | 4 | Katalina Oviedo | np_array = np.frombuffer(frame_data, dtype=np.uint8) |
| 107 | 4 | Katalina Oviedo | frame = cv2.imdecode(np_array, cv2.IMREAD_COLOR) |
| 108 | 4 | Katalina Oviedo | if frame is not None: |
| 109 | 4 | Katalina Oviedo | rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) |
| 110 | 4 | Katalina Oviedo | height, width, channel = rgb_frame.shape |
| 111 | 4 | Katalina Oviedo | q_image = QImage(rgb_frame.data, width, height, QImage.Format_RGB888) |
| 112 | 4 | Katalina Oviedo | self.frame_received.emit(q_image) |
| 113 | 4 | Katalina Oviedo | |
| 114 | 4 | Katalina Oviedo | except Exception as e: |
| 115 | 4 | Katalina Oviedo | print("Error en la recepción de video:", e) |
| 116 | 4 | Katalina Oviedo | finally: |
| 117 | 4 | Katalina Oviedo | s.close() |
| 118 | 4 | Katalina Oviedo | |
| 119 | 4 | Katalina Oviedo | def stop(self): |
| 120 | 4 | Katalina Oviedo | self.running = False |
| 121 | 4 | Katalina Oviedo | self.wait() |
| 122 | 4 | Katalina Oviedo | |
| 123 | 4 | Katalina Oviedo | class EnviarMensaje(QThread): |
| 124 | 4 | Katalina Oviedo | """ |
| 125 | 4 | Katalina Oviedo | Clase para enviar mensajes al servidor utilizando una conexión persistente |
| 126 | 4 | Katalina Oviedo | y una cola para optimizar el envío. |
| 127 | 4 | Katalina Oviedo | """ |
| 128 | 4 | Katalina Oviedo | def __init__(self, host, port): |
| 129 | 4 | Katalina Oviedo | super().__init__() |
| 130 | 4 | Katalina Oviedo | self.host = host |
| 131 | 4 | Katalina Oviedo | self.port = port |
| 132 | 4 | Katalina Oviedo | self.running = True |
| 133 | 4 | Katalina Oviedo | self.queue = Queue() # Cola para almacenar mensajes a enviar |
| 134 | 4 | Katalina Oviedo | self.socket = None # Conexión persistente con el servidor |
| 135 | 4 | Katalina Oviedo | |
| 136 | 4 | Katalina Oviedo | def run(self): |
| 137 | 4 | Katalina Oviedo | """ |
| 138 | 4 | Katalina Oviedo | Mantiene una conexión persistente con el servidor y envía los mensajes |
| 139 | 4 | Katalina Oviedo | de la cola según se van añadiendo. |
| 140 | 4 | Katalina Oviedo | """ |
| 141 | 4 | Katalina Oviedo | try: |
| 142 | 4 | Katalina Oviedo | # Crear la conexión persistente con el servidor |
| 143 | 4 | Katalina Oviedo | self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
| 144 | 4 | Katalina Oviedo | self.socket.settimeout(5) |
| 145 | 4 | Katalina Oviedo | self.socket.connect((self.host, self.port)) |
| 146 | 4 | Katalina Oviedo | print(f"Conexión persistente establecida con {self.host}:{self.port}") |
| 147 | 4 | Katalina Oviedo | |
| 148 | 4 | Katalina Oviedo | while self.running: |
| 149 | 4 | Katalina Oviedo | try: |
| 150 | 4 | Katalina Oviedo | # Obtener un mensaje de la cola (con timeout para evitar bloqueos infinitos) |
| 151 | 4 | Katalina Oviedo | mensaje = self.queue.get(timeout=1) |
| 152 | 4 | Katalina Oviedo | |
| 153 | 4 | Katalina Oviedo | # Si se recibe None, indica que el hilo debe detenerse |
| 154 | 4 | Katalina Oviedo | if mensaje is None: |
| 155 | 4 | Katalina Oviedo | break |
| 156 | 4 | Katalina Oviedo | |
| 157 | 4 | Katalina Oviedo | # Enviar el mensaje al servidor |
| 158 | 4 | Katalina Oviedo | self.socket.sendall(mensaje.encode('utf-8')) |
| 159 | 4 | Katalina Oviedo | print(f"Mensaje enviado: {mensaje}") |
| 160 | 4 | Katalina Oviedo | |
| 161 | 4 | Katalina Oviedo | except Exception as e: |
| 162 | 4 | Katalina Oviedo | print(f"Ante la espera: {e}") |
| 163 | 4 | Katalina Oviedo | continue |
| 164 | 4 | Katalina Oviedo | |
| 165 | 4 | Katalina Oviedo | except Exception as e: |
| 166 | 4 | Katalina Oviedo | print(f"Error al establecer conexión persistente: {e}") |
| 167 | 4 | Katalina Oviedo | finally: |
| 168 | 4 | Katalina Oviedo | # Asegurarse de cerrar la conexión al finalizar |
| 169 | 4 | Katalina Oviedo | if self.socket: |
| 170 | 4 | Katalina Oviedo | self.socket.close() |
| 171 | 4 | Katalina Oviedo | print("Conexión cerrada.") |
| 172 | 4 | Katalina Oviedo | |
| 173 | 4 | Katalina Oviedo | def enviar_mensaje(self, mensaje): |
| 174 | 4 | Katalina Oviedo | """ |
| 175 | 4 | Katalina Oviedo | Coloca un mensaje en la cola para ser enviado. |
| 176 | 4 | Katalina Oviedo | """ |
| 177 | 4 | Katalina Oviedo | self.queue.put(mensaje) |
| 178 | 4 | Katalina Oviedo | |
| 179 | 4 | Katalina Oviedo | def stop(self): |
| 180 | 4 | Katalina Oviedo | """ |
| 181 | 4 | Katalina Oviedo | Detiene el hilo limpiamente. |
| 182 | 4 | Katalina Oviedo | """ |
| 183 | 4 | Katalina Oviedo | self.running = False |
| 184 | 4 | Katalina Oviedo | self.queue.put(None) # Coloca un marcador para desbloquear el bucle |
| 185 | 4 | Katalina Oviedo | self.wait() |
| 186 | 4 | Katalina Oviedo | |
| 187 | 4 | Katalina Oviedo | class AquaPiApp(QWidget): |
| 188 | 4 | Katalina Oviedo | def __init__(self): |
| 189 | 4 | Katalina Oviedo | super().__init__() |
| 190 | 4 | Katalina Oviedo | self.setWindowTitle("AquaPi") |
| 191 | 4 | Katalina Oviedo | self.setFixedSize(346.32, 750) |
| 192 | 4 | Katalina Oviedo | self.rangos = {'temperatura': {'min': 1.0, 'max': 100.0}, 'ph': {'min': 1.0, 'max': 100.0}, 'luz': {'min': 1.0, 'max': 200.0}} |
| 193 | 4 | Katalina Oviedo | self.estado_parametros = {"temperatura": True, "ph": True, "luz": True} # True = dentro del rango |
| 194 | 4 | Katalina Oviedo | self.cargar_estilos() |
| 195 | 4 | Katalina Oviedo | |
| 196 | 4 | Katalina Oviedo | self.stack = QStackedWidget(self) |
| 197 | 4 | Katalina Oviedo | self.setLayout(QVBoxLayout()) |
| 198 | 4 | Katalina Oviedo | self.layout().setContentsMargins(0, 0, 0, 0) |
| 199 | 4 | Katalina Oviedo | self.layout().setSpacing(0) |
| 200 | 4 | Katalina Oviedo | self.layout().addWidget(self.stack) |
| 201 | 4 | Katalina Oviedo | |
| 202 | 4 | Katalina Oviedo | # Crear widgets de monitoreo una sola vez |
| 203 | 4 | Katalina Oviedo | self.temp_label = QLabel("0°C") |
| 204 | 4 | Katalina Oviedo | self.ph_label = QLabel("0.0") |
| 205 | 4 | Katalina Oviedo | self.luz_label = QLabel("0%") |
| 206 | 4 | Katalina Oviedo | |
| 207 | 4 | Katalina Oviedo | self.data_receiver = None |
| 208 | 4 | Katalina Oviedo | |
| 209 | 4 | Katalina Oviedo | self.mostrar_pantalla_conexion() |
| 210 | 4 | Katalina Oviedo | |
| 211 | 4 | Katalina Oviedo | def mostrar_pantalla_conexion(self): |
| 212 | 4 | Katalina Oviedo | layout = QVBoxLayout() |
| 213 | 4 | Katalina Oviedo | |
| 214 | 4 | Katalina Oviedo | titulo = QLabel("AQUA PI") |
| 215 | 4 | Katalina Oviedo | titulo.setAlignment(Qt.AlignCenter) |
| 216 | 4 | Katalina Oviedo | titulo.setObjectName("titulo_aquapi") |
| 217 | 4 | Katalina Oviedo | |
| 218 | 4 | Katalina Oviedo | entry_ip = QLineEdit() |
| 219 | 4 | Katalina Oviedo | entry_ip.setObjectName("texto_ruta") |
| 220 | 4 | Katalina Oviedo | entry_ip.setPlaceholderText("Ingrese la IP del servidor...") |
| 221 | 4 | Katalina Oviedo | |
| 222 | 4 | Katalina Oviedo | entry_ip.returnPressed.connect(lambda: self.verificar_conexion(entry_ip.text())) |
| 223 | 4 | Katalina Oviedo | |
| 224 | 4 | Katalina Oviedo | btn_conexion = QPushButton("Establecer Conexión") |
| 225 | 4 | Katalina Oviedo | btn_conexion.setObjectName("boton_conectar") |
| 226 | 4 | Katalina Oviedo | btn_conexion.clicked.connect(lambda: self.verificar_conexion(entry_ip.text())) |
| 227 | 4 | Katalina Oviedo | |
| 228 | 4 | Katalina Oviedo | layout.addWidget(titulo) |
| 229 | 4 | Katalina Oviedo | layout.addWidget(entry_ip) |
| 230 | 4 | Katalina Oviedo | layout.addWidget(btn_conexion) |
| 231 | 4 | Katalina Oviedo | layout.setAlignment(Qt.AlignTop) |
| 232 | 4 | Katalina Oviedo | |
| 233 | 4 | Katalina Oviedo | pantalla_conexion = QWidget() |
| 234 | 4 | Katalina Oviedo | pantalla_conexion.setLayout(layout) |
| 235 | 4 | Katalina Oviedo | self.stack.addWidget(pantalla_conexion) |
| 236 | 4 | Katalina Oviedo | self.stack.setCurrentWidget(pantalla_conexion) |
| 237 | 4 | Katalina Oviedo | |
| 238 | 4 | Katalina Oviedo | def verificar_conexion(self, ip): |
| 239 | 4 | Katalina Oviedo | if not self.validar_ip(ip): |
| 240 | 4 | Katalina Oviedo | QMessageBox.critical(self, "Error de IP", "La IP ingresada no es válida.") |
| 241 | 4 | Katalina Oviedo | return |
| 242 | 4 | Katalina Oviedo | |
| 243 | 4 | Katalina Oviedo | try: |
| 244 | 4 | Katalina Oviedo | # Iniciar recepción de datos |
| 245 | 4 | Katalina Oviedo | self.data_receiver = DataReceiver(ip, 5560) |
| 246 | 4 | Katalina Oviedo | self.data_receiver.data_received.connect(self.actualizar_datos) # Conexión correcta |
| 247 | 4 | Katalina Oviedo | self.data_receiver.start() |
| 248 | 4 | Katalina Oviedo | |
| 249 | 4 | Katalina Oviedo | # Iniciar recepción de video |
| 250 | 4 | Katalina Oviedo | self.video_receiver = VideoReceiver(ip, 5561) |
| 251 | 4 | Katalina Oviedo | self.video_receiver.frame_received.connect(self.actualizar_video) |
| 252 | 4 | Katalina Oviedo | self.video_receiver.start() |
| 253 | 4 | Katalina Oviedo | |
| 254 | 4 | Katalina Oviedo | # Crear la instancia de EnviarMensaje |
| 255 | 4 | Katalina Oviedo | self.enviar_mensajes = EnviarMensaje(ip, 5562) |
| 256 | 4 | Katalina Oviedo | self.enviar_mensajes.start() |
| 257 | 4 | Katalina Oviedo | |
| 258 | 4 | Katalina Oviedo | |
| 259 | 4 | Katalina Oviedo | # Mostrar la pantalla principal |
| 260 | 4 | Katalina Oviedo | self.mostrar_pantalla_monitoreo() |
| 261 | 4 | Katalina Oviedo | |
| 262 | 4 | Katalina Oviedo | except Exception as e: |
| 263 | 4 | Katalina Oviedo | QMessageBox.critical(self, "Error de Conexión", f"No se pudo conectar al servidor: {e}") |
| 264 | 4 | Katalina Oviedo | |
| 265 | 4 | Katalina Oviedo | def validar_ip(self, ip): |
| 266 | 4 | Katalina Oviedo | # Verificar si la IP ingresada tiene un formato válido |
| 267 | 4 | Katalina Oviedo | ip_regex = re.compile(r"^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$") |
| 268 | 4 | Katalina Oviedo | return ip_regex.match(ip) is not None |
| 269 | 4 | Katalina Oviedo | |
| 270 | 4 | Katalina Oviedo | def mostrar_pantalla_monitoreo(self): |
| 271 | 4 | Katalina Oviedo | # Verificar si la pantalla de monitoreo ya existe |
| 272 | 4 | Katalina Oviedo | if hasattr(self, 'pantalla_monitoreo'): |
| 273 | 4 | Katalina Oviedo | self.stack.setCurrentWidget(self.pantalla_monitoreo) |
| 274 | 4 | Katalina Oviedo | return |
| 275 | 4 | Katalina Oviedo | |
| 276 | 4 | Katalina Oviedo | # Crear layout para la pantalla de monitoreo |
| 277 | 4 | Katalina Oviedo | layout = QVBoxLayout() |
| 278 | 4 | Katalina Oviedo | layout.setContentsMargins(0, 0, 0, 0) |
| 279 | 4 | Katalina Oviedo | |
| 280 | 4 | Katalina Oviedo | # Crear título |
| 281 | 4 | Katalina Oviedo | layout.addItem(QSpacerItem(20, 49, QSizePolicy.Minimum, QSizePolicy.Fixed)) |
| 282 | 4 | Katalina Oviedo | titulo_label = QLabel("AQUAPI", self) |
| 283 | 4 | Katalina Oviedo | titulo_label.setAlignment(Qt.AlignCenter) |
| 284 | 4 | Katalina Oviedo | titulo_label.setObjectName("titulo_pantalla") |
| 285 | 4 | Katalina Oviedo | layout.addWidget(titulo_label) |
| 286 | 4 | Katalina Oviedo | |
| 287 | 4 | Katalina Oviedo | layout.addItem(QSpacerItem(20, 49, QSizePolicy.Minimum, QSizePolicy.Fixed)) |
| 288 | 4 | Katalina Oviedo | |
| 289 | 4 | Katalina Oviedo | # Crear etiquetas para mostrar los valores (ya inicializadas) |
| 290 | 4 | Katalina Oviedo | self.temp_label = self.agregar_cuadro(layout, "assets/temperatura.png", "Temperatura", "0°C") |
| 291 | 4 | Katalina Oviedo | layout.addItem(QSpacerItem(20, 10)) |
| 292 | 4 | Katalina Oviedo | self.ph_label = self.agregar_cuadro(layout, "assets/ph.png", "pH", "0.0") |
| 293 | 4 | Katalina Oviedo | layout.addItem(QSpacerItem(20, 10)) |
| 294 | 4 | Katalina Oviedo | self.luz_label = self.agregar_cuadro(layout, "assets/luz.png", "Luz", "0%") |
| 295 | 4 | Katalina Oviedo | layout.addItem(QSpacerItem(20, 88)) |
| 296 | 4 | Katalina Oviedo | |
| 297 | 4 | Katalina Oviedo | # Crear barra inferior con botones |
| 298 | 4 | Katalina Oviedo | btn_transmision = self.img_button(texto="Transmision", ruta_imagen="assets/transmision.png", ancho=120, alto=140, callback=self.mostrar_pantalla_transmision) |
| 299 | 4 | Katalina Oviedo | btn_config = self.img_button(texto="Configuración", ruta_imagen="assets/config.png", ancho=120, alto=140, callback=self.mostrar_pantalla_config) |
| 300 | 4 | Katalina Oviedo | |
| 301 | 4 | Katalina Oviedo | rectangulo_inferior = QWidget(self) |
| 302 | 4 | Katalina Oviedo | rectangulo_inferior.setObjectName("rectangulo") |
| 303 | 4 | Katalina Oviedo | rectangulo_inferior.setFixedHeight(79) |
| 304 | 4 | Katalina Oviedo | |
| 305 | 4 | Katalina Oviedo | rectangulo_layout = QHBoxLayout(rectangulo_inferior) |
| 306 | 4 | Katalina Oviedo | rectangulo_layout.setContentsMargins(0, 0, 0, 0) |
| 307 | 4 | Katalina Oviedo | |
| 308 | 4 | Katalina Oviedo | rectangulo_layout.addWidget(btn_transmision) |
| 309 | 4 | Katalina Oviedo | rectangulo_layout.addWidget(btn_config) |
| 310 | 4 | Katalina Oviedo | |
| 311 | 4 | Katalina Oviedo | layout.addWidget(rectangulo_inferior) |
| 312 | 4 | Katalina Oviedo | |
| 313 | 4 | Katalina Oviedo | # Guardar la pantalla de monitoreo en un atributo |
| 314 | 4 | Katalina Oviedo | self.pantalla_monitoreo = QWidget() |
| 315 | 4 | Katalina Oviedo | self.pantalla_monitoreo.setLayout(layout) |
| 316 | 4 | Katalina Oviedo | self.stack.addWidget(self.pantalla_monitoreo) |
| 317 | 4 | Katalina Oviedo | self.stack.setCurrentWidget(self.pantalla_monitoreo) |
| 318 | 4 | Katalina Oviedo | |
| 319 | 4 | Katalina Oviedo | def actualizar_datos(self, temp, ph, light): |
| 320 | 4 | Katalina Oviedo | # Actualizar valores |
| 321 | 4 | Katalina Oviedo | self.temp_label.setText(f"{temp:.2f}°C") |
| 322 | 4 | Katalina Oviedo | self.ph_label.setText(f"{ph:.2f}") |
| 323 | 4 | Katalina Oviedo | self.luz_label.setText(f"{light}%") |
| 324 | 4 | Katalina Oviedo | |
| 325 | 4 | Katalina Oviedo | # Validar y cambiar colores |
| 326 | 4 | Katalina Oviedo | self.validar_y_cambiar_color(self.temp_label, "temperatura", temp, self.parametro_temp_label) |
| 327 | 4 | Katalina Oviedo | # Validar y actualizar los colores para pH |
| 328 | 4 | Katalina Oviedo | self.validar_y_cambiar_color(self.ph_label, "ph", ph, self.parametro_ph_label) |
| 329 | 4 | Katalina Oviedo | # Validar y actualizar los colores para luz |
| 330 | 4 | Katalina Oviedo | self.validar_y_cambiar_color(self.luz_label, "luz", light, self.parametro_luz_label) |
| 331 | 4 | Katalina Oviedo | |
| 332 | 4 | Katalina Oviedo | def validar_y_cambiar_color(self, label, parametro, valor, parametro_label): |
| 333 | 4 | Katalina Oviedo | """ |
| 334 | 4 | Katalina Oviedo | Valida el valor y cambia el color del frame y del label del parámetro si el valor no está en rango. |
| 335 | 4 | Katalina Oviedo | Envía un mensaje solo si el estado cambia (de dentro a fuera de rango o viceversa). |
| 336 | 4 | Katalina Oviedo | """ |
| 337 | 4 | Katalina Oviedo | valor_numerico = self.convertir_a_numero(valor) |
| 338 | 4 | Katalina Oviedo | dentro_de_rango = valor_numerico is not None and self.validar_valor(parametro, valor_numerico) |
| 339 | 4 | Katalina Oviedo | |
| 340 | 4 | Katalina Oviedo | # Si el estado cambia, actualizar el estado y enviar el mensaje correspondiente |
| 341 | 4 | Katalina Oviedo | if self.estado_parametros[parametro] != dentro_de_rango and parametro == "ph": |
| 342 | 4 | Katalina Oviedo | self.estado_parametros[parametro] = dentro_de_rango |
| 343 | 4 | Katalina Oviedo | mensaje = "RELAY_ON" if not dentro_de_rango else "RELAY_OFF" |
| 344 | 4 | Katalina Oviedo | self.enviar_mensajes.enviar_mensaje(mensaje) |
| 345 | 4 | Katalina Oviedo | |
| 346 | 4 | Katalina Oviedo | # Cambiar estilos visuales según el estado |
| 347 | 4 | Katalina Oviedo | if dentro_de_rango: |
| 348 | 4 | Katalina Oviedo | label.parentWidget().setStyleSheet( |
| 349 | 4 | Katalina Oviedo | "border: 8px solid; border-radius: 20px; background-color: #564D80; border-color: #564D80;" |
| 350 | 4 | Katalina Oviedo | ) |
| 351 | 4 | Katalina Oviedo | parametro_label.setStyleSheet("color: white; font-size: 21px") |
| 352 | 4 | Katalina Oviedo | else: |
| 353 | 4 | Katalina Oviedo | label.parentWidget().setStyleSheet( |
| 354 | 4 | Katalina Oviedo | "border: 8px solid; border-radius: 20px; background-color: #984F4F; border-color: #984F4F;" |
| 355 | 4 | Katalina Oviedo | ) |
| 356 | 4 | Katalina Oviedo | parametro_label.setStyleSheet("color: #EF5E5E; font-size: 21px") |
| 357 | 4 | Katalina Oviedo | |
| 358 | 4 | Katalina Oviedo | def validar_valor(self, parametro, valor): |
| 359 | 4 | Katalina Oviedo | """Valida si el valor está dentro del rango configurado para el parámetro.""" |
| 360 | 4 | Katalina Oviedo | # Convertimos el parámetro a minúsculas para asegurar consistencia |
| 361 | 4 | Katalina Oviedo | parametro_lower = parametro.lower() |
| 362 | 4 | Katalina Oviedo | # Verificamos si el parámetro existe en los rangos |
| 363 | 4 | Katalina Oviedo | if parametro_lower not in self.rangos: |
| 364 | 4 | Katalina Oviedo | return False |
| 365 | 4 | Katalina Oviedo | # Accedemos al rango correspondiente |
| 366 | 4 | Katalina Oviedo | rango = self.rangos[parametro_lower] # Asegurando que se use la clave correcta |
| 367 | 4 | Katalina Oviedo | # Verificamos que el valor esté en el formato adecuado (convertirlo a float si es necesario) |
| 368 | 4 | Katalina Oviedo | try: |
| 369 | 4 | Katalina Oviedo | valor = float(valor) # Asegúrate de que 'valor' sea un número |
| 370 | 4 | Katalina Oviedo | except ValueError: |
| 371 | 4 | Katalina Oviedo | print(f"Valor no válido: {valor}") |
| 372 | 4 | Katalina Oviedo | return False |
| 373 | 4 | Katalina Oviedo | # Comprobamos si el valor está fuera del rango |
| 374 | 4 | Katalina Oviedo | if valor < rango['min'] or valor > rango['max']: |
| 375 | 4 | Katalina Oviedo | return False # Valor fuera de rango |
| 376 | 4 | Katalina Oviedo | else: |
| 377 | 4 | Katalina Oviedo | return True # Valor dentro del rango |
| 378 | 4 | Katalina Oviedo | |
| 379 | 4 | Katalina Oviedo | def closeEvent(self, event): |
| 380 | 4 | Katalina Oviedo | if self.data_receiver: |
| 381 | 4 | Katalina Oviedo | self.data_receiver.stop() |
| 382 | 4 | Katalina Oviedo | if hasattr(self, 'video_receiver') and self.video_receiver: |
| 383 | 4 | Katalina Oviedo | self.video_receiver.stop() |
| 384 | 4 | Katalina Oviedo | super().closeEvent(event) |
| 385 | 4 | Katalina Oviedo | |
| 386 | 4 | Katalina Oviedo | def actualizar_video(self, frame): |
| 387 | 4 | Katalina Oviedo | if self.video_label: |
| 388 | 4 | Katalina Oviedo | self.video_label.setPixmap(QPixmap.fromImage(frame)) |
| 389 | 4 | Katalina Oviedo | |
| 390 | 4 | Katalina Oviedo | def mostrar_pantalla_transmision(self): |
| 391 | 4 | Katalina Oviedo | layout = QVBoxLayout() |
| 392 | 4 | Katalina Oviedo | layout.setContentsMargins(0, 0, 0, 0) |
| 393 | 4 | Katalina Oviedo | |
| 394 | 4 | Katalina Oviedo | layout.addItem(QSpacerItem(20, 49, QSizePolicy.Minimum, QSizePolicy.Fixed)) |
| 395 | 4 | Katalina Oviedo | |
| 396 | 4 | Katalina Oviedo | # Título de la pantalla |
| 397 | 4 | Katalina Oviedo | titulo_label = QLabel("AQUAPI", self) |
| 398 | 4 | Katalina Oviedo | titulo_label.setAlignment(Qt.AlignCenter) |
| 399 | 4 | Katalina Oviedo | titulo_label.setObjectName("titulo_pantalla") |
| 400 | 4 | Katalina Oviedo | layout.addWidget(titulo_label) |
| 401 | 4 | Katalina Oviedo | |
| 402 | 4 | Katalina Oviedo | # Espaciado debajo del título |
| 403 | 4 | Katalina Oviedo | layout.addItem(QSpacerItem(20, 134, QSizePolicy.Minimum, QSizePolicy.Fixed)) |
| 404 | 4 | Katalina Oviedo | |
| 405 | 4 | Katalina Oviedo | self.video_label = QLabel(self) |
| 406 | 4 | Katalina Oviedo | self.video_label.setFixedSize(318, 242) |
| 407 | 4 | Katalina Oviedo | self.video_label.setStyleSheet("background-color: black;") |
| 408 | 4 | Katalina Oviedo | self.video_label.setAlignment(Qt.AlignCenter) # Asegura que el contenido del QLabel esté centrado |
| 409 | 4 | Katalina Oviedo | layout.addWidget(self.video_label, alignment=Qt.AlignCenter) |
| 410 | 4 | Katalina Oviedo | |
| 411 | 4 | Katalina Oviedo | # Espaciado debajo del rectángulo |
| 412 | 4 | Katalina Oviedo | layout.addItem(QSpacerItem(0, 206, QSizePolicy.Minimum, QSizePolicy.Fixed)) |
| 413 | 4 | Katalina Oviedo | |
| 414 | 4 | Katalina Oviedo | # Botones de configuración y monitoreo |
| 415 | 4 | Katalina Oviedo | btn_config = self.img_button(texto="Configuración", ruta_imagen="assets/config.png", ancho=120, alto=140, callback=self.mostrar_pantalla_config) |
| 416 | 4 | Katalina Oviedo | btn_monitoreo = self.img_button(texto="Monitoreo", ruta_imagen="assets/monitoreo.png", ancho=120, alto=140, callback=self.mostrar_pantalla_monitoreo) |
| 417 | 4 | Katalina Oviedo | |
| 418 | 4 | Katalina Oviedo | # Rectángulo inferior con los botones |
| 419 | 4 | Katalina Oviedo | rectangulo_inferior = QWidget(self) |
| 420 | 4 | Katalina Oviedo | rectangulo_inferior.setObjectName("rectangulo") |
| 421 | 4 | Katalina Oviedo | rectangulo_inferior.setFixedHeight(79) |
| 422 | 4 | Katalina Oviedo | |
| 423 | 4 | Katalina Oviedo | rectangulo_layout_inferior = QHBoxLayout(rectangulo_inferior) |
| 424 | 4 | Katalina Oviedo | rectangulo_layout_inferior.setContentsMargins(0, 0, 0, 0) |
| 425 | 4 | Katalina Oviedo | |
| 426 | 4 | Katalina Oviedo | rectangulo_layout_inferior.addWidget(btn_config) |
| 427 | 4 | Katalina Oviedo | rectangulo_layout_inferior.addItem(QSpacerItem(4, 0, QSizePolicy.Minimum, QSizePolicy.Fixed)) |
| 428 | 4 | Katalina Oviedo | rectangulo_layout_inferior.addWidget(btn_monitoreo) |
| 429 | 4 | Katalina Oviedo | rectangulo_layout_inferior.addItem(QSpacerItem(14, 0, QSizePolicy.Minimum, QSizePolicy.Fixed)) |
| 430 | 4 | Katalina Oviedo | |
| 431 | 4 | Katalina Oviedo | layout.addWidget(rectangulo_inferior) |
| 432 | 4 | Katalina Oviedo | |
| 433 | 4 | Katalina Oviedo | # Crear el widget final y agregarlo al stack |
| 434 | 4 | Katalina Oviedo | pantalla_transmision = QWidget() |
| 435 | 4 | Katalina Oviedo | pantalla_transmision.setLayout(layout) |
| 436 | 4 | Katalina Oviedo | self.stack.addWidget(pantalla_transmision) |
| 437 | 4 | Katalina Oviedo | |
| 438 | 4 | Katalina Oviedo | # Mostrar la pantalla |
| 439 | 4 | Katalina Oviedo | self.stack.setCurrentWidget(pantalla_transmision) |
| 440 | 4 | Katalina Oviedo | |
| 441 | 4 | Katalina Oviedo | def convertir_a_numero(self, valor): |
| 442 | 4 | Katalina Oviedo | """ |
| 443 | 4 | Katalina Oviedo | Convierte un valor a un número, eliminando caracteres extra como '°C' o '%'. |
| 444 | 4 | Katalina Oviedo | Si no es posible convertirlo, devuelve None. |
| 445 | 4 | Katalina Oviedo | """ |
| 446 | 4 | Katalina Oviedo | # Si el valor ya es un número, simplemente devolverlo |
| 447 | 4 | Katalina Oviedo | if isinstance(valor, (int, float)): |
| 448 | 4 | Katalina Oviedo | return valor |
| 449 | 4 | Katalina Oviedo | |
| 450 | 4 | Katalina Oviedo | # Si es una cadena, intentar convertirla |
| 451 | 4 | Katalina Oviedo | try: |
| 452 | 4 | Katalina Oviedo | if isinstance(valor, str): |
| 453 | 4 | Katalina Oviedo | # Eliminar caracteres específicos como '°C' o '%' |
| 454 | 4 | Katalina Oviedo | if '°C' in valor: |
| 455 | 4 | Katalina Oviedo | valor = valor.replace('°C', '').strip() |
| 456 | 4 | Katalina Oviedo | if '%' in valor: |
| 457 | 4 | Katalina Oviedo | valor = valor.replace('%', '').strip() |
| 458 | 4 | Katalina Oviedo | # Convertir la cadena a un número |
| 459 | 4 | Katalina Oviedo | return float(valor) |
| 460 | 4 | Katalina Oviedo | except (ValueError, TypeError): |
| 461 | 4 | Katalina Oviedo | print("Error al convertir el valor:", valor) |
| 462 | 4 | Katalina Oviedo | return None |
| 463 | 4 | Katalina Oviedo | |
| 464 | 4 | Katalina Oviedo | def img_button(self, texto, ruta_imagen, ancho=120, alto=140, callback=None): |
| 465 | 4 | Katalina Oviedo | boton = QPushButton() |
| 466 | 4 | Katalina Oviedo | boton.setObjectName("boton_accion") |
| 467 | 4 | Katalina Oviedo | boton.setFixedSize(ancho, alto) |
| 468 | 4 | Katalina Oviedo | |
| 469 | 4 | Katalina Oviedo | layout = QVBoxLayout(boton) |
| 470 | 4 | Katalina Oviedo | layout.setContentsMargins(0, 0, 0, 0) |
| 471 | 4 | Katalina Oviedo | layout.setSpacing(0) |
| 472 | 4 | Katalina Oviedo | |
| 473 | 4 | Katalina Oviedo | label_imagen = QLabel() |
| 474 | 4 | Katalina Oviedo | pixmap = QPixmap(ruta_imagen).scaled(50, 50, Qt.KeepAspectRatio, Qt.SmoothTransformation) |
| 475 | 4 | Katalina Oviedo | label_imagen.setPixmap(pixmap) |
| 476 | 4 | Katalina Oviedo | label_imagen.setAlignment(Qt.AlignCenter) |
| 477 | 4 | Katalina Oviedo | label_imagen.setMinimumSize(31, 31) |
| 478 | 4 | Katalina Oviedo | label_imagen.setStyleSheet("background-color: none") |
| 479 | 4 | Katalina Oviedo | layout.addWidget(label_imagen) |
| 480 | 4 | Katalina Oviedo | |
| 481 | 4 | Katalina Oviedo | label_texto = QLabel(texto) |
| 482 | 4 | Katalina Oviedo | label_texto.setAlignment(Qt.AlignCenter) |
| 483 | 4 | Katalina Oviedo | label_texto.setStyleSheet("color: white; font-size: 14px; background-color: transparent; font-weight: normal") |
| 484 | 4 | Katalina Oviedo | layout.addWidget(label_texto) |
| 485 | 4 | Katalina Oviedo | |
| 486 | 4 | Katalina Oviedo | layout.setSizeConstraint(QLayout.SetFixedSize) |
| 487 | 4 | Katalina Oviedo | |
| 488 | 4 | Katalina Oviedo | if callback: |
| 489 | 4 | Katalina Oviedo | boton.clicked.connect(callback) |
| 490 | 4 | Katalina Oviedo | |
| 491 | 4 | Katalina Oviedo | return boton |
| 492 | 4 | Katalina Oviedo | |
| 493 | 4 | Katalina Oviedo | def input_frame(self, layout, ancho, alto, texto_bajo): |
| 494 | 4 | Katalina Oviedo | """Crea un cuadro de entrada estilizado con un texto debajo y lo agrega al layout dado.""" |
| 495 | 4 | Katalina Oviedo | cuadro_layout = QVBoxLayout() |
| 496 | 4 | Katalina Oviedo | |
| 497 | 4 | Katalina Oviedo | # Crear el marco estilizado |
| 498 | 4 | Katalina Oviedo | frame = QWidget(self) |
| 499 | 4 | Katalina Oviedo | frame.setStyleSheet("border: 8px solid; border-radius: 20px; background-color: #564D80; border-color: #564D80;") |
| 500 | 4 | Katalina Oviedo | frame.setFixedSize(ancho, alto) |
| 501 | 4 | Katalina Oviedo | |
| 502 | 4 | Katalina Oviedo | frame_layout = QVBoxLayout(frame) |
| 503 | 4 | Katalina Oviedo | frame_layout.setSpacing(0) |
| 504 | 4 | Katalina Oviedo | frame_layout.setContentsMargins(0, 0, 0, 0) |
| 505 | 4 | Katalina Oviedo | |
| 506 | 4 | Katalina Oviedo | # Crear el campo de entrada |
| 507 | 4 | Katalina Oviedo | input_field = QLineEdit(frame) |
| 508 | 4 | Katalina Oviedo | |
| 509 | 4 | Katalina Oviedo | # Función para validar manualmente el texto del input |
| 510 | 4 | Katalina Oviedo | def validar_entrada(): |
| 511 | 4 | Katalina Oviedo | texto = input_field.text() |
| 512 | 4 | Katalina Oviedo | # Reemplazar comas por puntos |
| 513 | 4 | Katalina Oviedo | texto = texto.replace(",", ".") |
| 514 | 4 | Katalina Oviedo | # Validar si el texto es un número flotante válido |
| 515 | 4 | Katalina Oviedo | try: |
| 516 | 4 | Katalina Oviedo | valor = float(texto) |
| 517 | 4 | Katalina Oviedo | # Formatear el número para que muestre hasta dos decimales, pero no añada decimales innecesarios |
| 518 | 4 | Katalina Oviedo | if valor.is_integer(): |
| 519 | 4 | Katalina Oviedo | input_field.setText(f"{int(valor)}") # Si es un número entero, mostrar sin decimales |
| 520 | 4 | Katalina Oviedo | else: |
| 521 | 4 | Katalina Oviedo | input_field.setText(f"{valor:.2f}") # Si tiene decimales, mostrar hasta dos decimales |
| 522 | 4 | Katalina Oviedo | |
| 523 | 4 | Katalina Oviedo | except ValueError: |
| 524 | 4 | Katalina Oviedo | # Si no es un número válido, podemos vaciar el campo o dejarlo como está |
| 525 | 4 | Katalina Oviedo | input_field.setText("") # O, si prefieres, deja el valor sin cambiar |
| 526 | 4 | Katalina Oviedo | return |
| 527 | 4 | Katalina Oviedo | |
| 528 | 4 | Katalina Oviedo | # Conectar el evento de perder el foco (cuando el usuario termina de editar) |
| 529 | 4 | Katalina Oviedo | input_field.editingFinished.connect(validar_entrada) |
| 530 | 4 | Katalina Oviedo | |
| 531 | 4 | Katalina Oviedo | input_field.setAlignment(Qt.AlignCenter) # Centrar el texto ingresado |
| 532 | 4 | Katalina Oviedo | input_field.setObjectName("input_config") # Identificador para estilos |
| 533 | 4 | Katalina Oviedo | frame_layout.addWidget(input_field) |
| 534 | 4 | Katalina Oviedo | |
| 535 | 4 | Katalina Oviedo | # Agregar el marco al diseño principal |
| 536 | 4 | Katalina Oviedo | cuadro_layout.addWidget(frame, alignment=Qt.AlignHCenter) |
| 537 | 4 | Katalina Oviedo | |
| 538 | 4 | Katalina Oviedo | # Crear la etiqueta debajo del input |
| 539 | 4 | Katalina Oviedo | label_bajo = QLabel(texto_bajo, self) |
| 540 | 4 | Katalina Oviedo | label_bajo.setAlignment(Qt.AlignCenter) |
| 541 | 4 | Katalina Oviedo | label_bajo.setStyleSheet("color: white; font-size: 21px; background-color: none; margin-top: 5px") |
| 542 | 4 | Katalina Oviedo | cuadro_layout.addWidget(label_bajo, alignment=Qt.AlignHCenter) |
| 543 | 4 | Katalina Oviedo | |
| 544 | 4 | Katalina Oviedo | # Agregar el cuadro al diseño recibido |
| 545 | 4 | Katalina Oviedo | layout.addLayout(cuadro_layout) |
| 546 | 4 | Katalina Oviedo | |
| 547 | 4 | Katalina Oviedo | # Devolver el campo de entrada para seguimiento |
| 548 | 4 | Katalina Oviedo | return input_field |
| 549 | 4 | Katalina Oviedo | |
| 550 | 4 | Katalina Oviedo | def mostrar_pantalla_config(self): |
| 551 | 4 | Katalina Oviedo | layout = QVBoxLayout() |
| 552 | 4 | Katalina Oviedo | layout.setContentsMargins(0, 0, 0, 0) |
| 553 | 4 | Katalina Oviedo | |
| 554 | 4 | Katalina Oviedo | # Título |
| 555 | 4 | Katalina Oviedo | layout.addItem(QSpacerItem(20, 49, QSizePolicy.Minimum, QSizePolicy.Fixed)) |
| 556 | 4 | Katalina Oviedo | titulo_label = QLabel("AQUAPI", self) |
| 557 | 4 | Katalina Oviedo | titulo_label.setAlignment(Qt.AlignCenter) |
| 558 | 4 | Katalina Oviedo | titulo_label.setObjectName("titulo_pantalla") |
| 559 | 4 | Katalina Oviedo | layout.addWidget(titulo_label) |
| 560 | 4 | Katalina Oviedo | layout.addItem(QSpacerItem(20, 30, QSizePolicy.Minimum, QSizePolicy.Fixed)) |
| 561 | 4 | Katalina Oviedo | |
| 562 | 4 | Katalina Oviedo | # Inputs |
| 563 | 4 | Katalina Oviedo | self.inputs = [] # Lista para almacenar referencias a los inputs |
| 564 | 4 | Katalina Oviedo | |
| 565 | 4 | Katalina Oviedo | fila1_layout = QHBoxLayout() |
| 566 | 4 | Katalina Oviedo | label_temp = QLabel("Temperatura") |
| 567 | 4 | Katalina Oviedo | label_temp.setObjectName("parametro") |
| 568 | 4 | Katalina Oviedo | label_temp.setAlignment(Qt.AlignCenter) |
| 569 | 4 | Katalina Oviedo | layout.addWidget(label_temp) |
| 570 | 4 | Katalina Oviedo | input_min_temp = self.input_frame(fila1_layout, 116, 68, "Min") |
| 571 | 4 | Katalina Oviedo | input_max_temp = self.input_frame(fila1_layout, 116, 68, "Máx") |
| 572 | 4 | Katalina Oviedo | self.inputs.append(input_min_temp) |
| 573 | 4 | Katalina Oviedo | self.inputs.append(input_max_temp) |
| 574 | 4 | Katalina Oviedo | layout.addLayout(fila1_layout) |
| 575 | 4 | Katalina Oviedo | layout.addItem(QSpacerItem(0, 2, QSizePolicy.Minimum, QSizePolicy.Fixed)) |
| 576 | 4 | Katalina Oviedo | |
| 577 | 4 | Katalina Oviedo | fila2_layout = QHBoxLayout() |
| 578 | 4 | Katalina Oviedo | label_ph = QLabel("pH") |
| 579 | 4 | Katalina Oviedo | label_ph.setObjectName("parametro") |
| 580 | 4 | Katalina Oviedo | label_ph.setAlignment(Qt.AlignCenter) |
| 581 | 4 | Katalina Oviedo | layout.addWidget(label_ph) |
| 582 | 4 | Katalina Oviedo | input_min_ph = self.input_frame(fila2_layout, 116, 68, "Min") |
| 583 | 4 | Katalina Oviedo | input_max_ph = self.input_frame(fila2_layout, 116, 68, "Máx") |
| 584 | 4 | Katalina Oviedo | self.inputs.append(input_min_ph) |
| 585 | 4 | Katalina Oviedo | self.inputs.append(input_max_ph) |
| 586 | 4 | Katalina Oviedo | layout.addLayout(fila2_layout) |
| 587 | 4 | Katalina Oviedo | layout.addItem(QSpacerItem(0, 5, QSizePolicy.Minimum, QSizePolicy.Fixed)) |
| 588 | 4 | Katalina Oviedo | |
| 589 | 4 | Katalina Oviedo | fila3_layout = QHBoxLayout() |
| 590 | 4 | Katalina Oviedo | label_luz = QLabel("Luz") |
| 591 | 4 | Katalina Oviedo | label_luz.setObjectName("parametro") |
| 592 | 4 | Katalina Oviedo | label_luz.setAlignment(Qt.AlignCenter) |
| 593 | 4 | Katalina Oviedo | layout.addWidget(label_luz) |
| 594 | 4 | Katalina Oviedo | input_min_luz = self.input_frame(fila3_layout, 116, 68, "Min") |
| 595 | 4 | Katalina Oviedo | input_max_luz = self.input_frame(fila3_layout, 116, 68, "Máx") |
| 596 | 4 | Katalina Oviedo | self.inputs.append(input_min_luz) |
| 597 | 4 | Katalina Oviedo | self.inputs.append(input_max_luz) |
| 598 | 4 | Katalina Oviedo | layout.addLayout(fila3_layout) |
| 599 | 4 | Katalina Oviedo | layout.addItem(QSpacerItem(0, 20, QSizePolicy.Minimum, QSizePolicy.Fixed)) |
| 600 | 4 | Katalina Oviedo | |
| 601 | 4 | Katalina Oviedo | # Rellenar los valores en los inputs desde self.rangos |
| 602 | 4 | Katalina Oviedo | if self.rangos['temperatura']['min'] is not None: |
| 603 | 4 | Katalina Oviedo | self.inputs[0].setText(str(self.rangos['temperatura']['min'])) |
| 604 | 4 | Katalina Oviedo | if self.rangos['temperatura']['max'] is not None: |
| 605 | 4 | Katalina Oviedo | self.inputs[1].setText(str(self.rangos['temperatura']['max'])) |
| 606 | 4 | Katalina Oviedo | |
| 607 | 4 | Katalina Oviedo | if self.rangos['ph']['min'] is not None: |
| 608 | 4 | Katalina Oviedo | self.inputs[2].setText(str(self.rangos['ph']['min'])) |
| 609 | 4 | Katalina Oviedo | if self.rangos['ph']['max'] is not None: |
| 610 | 4 | Katalina Oviedo | self.inputs[3].setText(str(self.rangos['ph']['max'])) |
| 611 | 4 | Katalina Oviedo | |
| 612 | 4 | Katalina Oviedo | if self.rangos['luz']['min'] is not None: |
| 613 | 4 | Katalina Oviedo | self.inputs[4].setText(str(self.rangos['luz']['min'])) |
| 614 | 4 | Katalina Oviedo | if self.rangos['luz']['max'] is not None: |
| 615 | 4 | Katalina Oviedo | self.inputs[5].setText(str(self.rangos['luz']['max'])) |
| 616 | 4 | Katalina Oviedo | |
| 617 | 4 | Katalina Oviedo | # Botón Confirmar |
| 618 | 4 | Katalina Oviedo | confirmar_btn = QPushButton("Confirmar", self) |
| 619 | 4 | Katalina Oviedo | confirmar_btn.setObjectName("confirmar_btn") |
| 620 | 4 | Katalina Oviedo | confirmar_btn.clicked.connect(self.enviar_datos) # Conectar el evento del botón |
| 621 | 4 | Katalina Oviedo | layout.addWidget(confirmar_btn, alignment=Qt.AlignCenter) |
| 622 | 4 | Katalina Oviedo | |
| 623 | 4 | Katalina Oviedo | # Espaciador antes del rectángulo inferior |
| 624 | 4 | Katalina Oviedo | layout.addItem(QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Fixed)) |
| 625 | 4 | Katalina Oviedo | |
| 626 | 4 | Katalina Oviedo | # Rectángulo inferior con botones |
| 627 | 4 | Katalina Oviedo | pantalla_config = QWidget() |
| 628 | 4 | Katalina Oviedo | pantalla_config.setLayout(layout) |
| 629 | 4 | Katalina Oviedo | self.stack.addWidget(pantalla_config) |
| 630 | 4 | Katalina Oviedo | self.stack.setCurrentWidget(pantalla_config) |
| 631 | 4 | Katalina Oviedo | |
| 632 | 4 | Katalina Oviedo | btn_monitoreo = self.img_button( |
| 633 | 4 | Katalina Oviedo | texto="Monitoreo", |
| 634 | 4 | Katalina Oviedo | ruta_imagen="assets/monitoreo.png", |
| 635 | 4 | Katalina Oviedo | ancho=120, |
| 636 | 4 | Katalina Oviedo | alto=140, |
| 637 | 4 | Katalina Oviedo | callback=self.mostrar_pantalla_monitoreo |
| 638 | 4 | Katalina Oviedo | ) |
| 639 | 4 | Katalina Oviedo | btn_transmision = self.img_button( |
| 640 | 4 | Katalina Oviedo | texto="Transmision", |
| 641 | 4 | Katalina Oviedo | ruta_imagen="assets/transmision.png", |
| 642 | 4 | Katalina Oviedo | ancho=120, |
| 643 | 4 | Katalina Oviedo | alto=140, |
| 644 | 4 | Katalina Oviedo | callback=self.mostrar_pantalla_transmision |
| 645 | 4 | Katalina Oviedo | ) |
| 646 | 4 | Katalina Oviedo | |
| 647 | 4 | Katalina Oviedo | rectangulo_inferior = QWidget(self) |
| 648 | 4 | Katalina Oviedo | rectangulo_inferior.setObjectName("rectangulo") |
| 649 | 4 | Katalina Oviedo | rectangulo_inferior.setFixedHeight(79) |
| 650 | 4 | Katalina Oviedo | |
| 651 | 4 | Katalina Oviedo | rectangulo_layout = QHBoxLayout(rectangulo_inferior) |
| 652 | 4 | Katalina Oviedo | rectangulo_layout.setContentsMargins(0, 0, 0, 0) |
| 653 | 4 | Katalina Oviedo | |
| 654 | 4 | Katalina Oviedo | rectangulo_layout.addWidget(btn_monitoreo) |
| 655 | 4 | Katalina Oviedo | rectangulo_layout.addItem(QSpacerItem(10, 0, QSizePolicy.Minimum, QSizePolicy.Fixed)) |
| 656 | 4 | Katalina Oviedo | rectangulo_layout.addWidget(btn_transmision) |
| 657 | 4 | Katalina Oviedo | rectangulo_layout.addItem(QSpacerItem(10, 0, QSizePolicy.Minimum, QSizePolicy.Fixed)) |
| 658 | 4 | Katalina Oviedo | |
| 659 | 4 | Katalina Oviedo | layout.addWidget(rectangulo_inferior) |
| 660 | 4 | Katalina Oviedo | |
| 661 | 4 | Katalina Oviedo | def agregar_cuadro(self, layout, icono_path, parametro, valor): |
| 662 | 4 | Katalina Oviedo | # Crear el frame y asignarle un objectName único |
| 663 | 4 | Katalina Oviedo | frame = QWidget(self) |
| 664 | 4 | Katalina Oviedo | frame.setObjectName(parametro.lower()) # Asegurando que esté en minúsculas |
| 665 | 4 | Katalina Oviedo | frame.setStyleSheet(f"border: 8px solid; border-radius: 20px; background-color: #564D80; border-color: #564D80;") |
| 666 | 4 | Katalina Oviedo | |
| 667 | 4 | Katalina Oviedo | frame_layout = QHBoxLayout(frame) |
| 668 | 4 | Katalina Oviedo | frame_layout.setSpacing(0) |
| 669 | 4 | Katalina Oviedo | frame_layout.setContentsMargins(0, 0, 0, 0) |
| 670 | 4 | Katalina Oviedo | frame.setFixedSize(262, 94) |
| 671 | 4 | Katalina Oviedo | |
| 672 | 4 | Katalina Oviedo | icono_label = QLabel(frame) |
| 673 | 4 | Katalina Oviedo | pixmap = QPixmap(icono_path).scaled(57, 57, Qt.KeepAspectRatio, Qt.SmoothTransformation) |
| 674 | 4 | Katalina Oviedo | icono_label.setPixmap(pixmap) |
| 675 | 4 | Katalina Oviedo | icono_label.setObjectName("icono_label") |
| 676 | 4 | Katalina Oviedo | frame_layout.addWidget(icono_label) |
| 677 | 4 | Katalina Oviedo | |
| 678 | 4 | Katalina Oviedo | text_layout = QVBoxLayout() |
| 679 | 4 | Katalina Oviedo | valor_label = QLabel(valor, frame) |
| 680 | 4 | Katalina Oviedo | valor_label.setObjectName("valor_label") |
| 681 | 4 | Katalina Oviedo | text_layout.addWidget(valor_label) |
| 682 | 4 | Katalina Oviedo | frame_layout.addLayout(text_layout) |
| 683 | 4 | Katalina Oviedo | text_layout.setSpacing(0) |
| 684 | 4 | Katalina Oviedo | text_layout.setContentsMargins(0, 0, 0, 0) |
| 685 | 4 | Katalina Oviedo | |
| 686 | 4 | Katalina Oviedo | # Agregar el frame al layout |
| 687 | 4 | Katalina Oviedo | layout.addWidget(frame, alignment=Qt.AlignHCenter) |
| 688 | 4 | Katalina Oviedo | |
| 689 | 4 | Katalina Oviedo | # Agregar el parámetro al layout |
| 690 | 4 | Katalina Oviedo | parametro_label = QLabel(parametro, self) |
| 691 | 4 | Katalina Oviedo | parametro_label.setAlignment(Qt.AlignCenter) |
| 692 | 4 | Katalina Oviedo | parametro_label.setStyleSheet("color: white ; font-size: 21px") |
| 693 | 4 | Katalina Oviedo | layout.addWidget(parametro_label) |
| 694 | 4 | Katalina Oviedo | |
| 695 | 4 | Katalina Oviedo | # Actualizar la etiqueta correspondiente según el parámetro |
| 696 | 4 | Katalina Oviedo | if parametro.lower() == "temperatura": |
| 697 | 4 | Katalina Oviedo | self.temp_label = valor_label |
| 698 | 4 | Katalina Oviedo | self.parametro_temp_label = parametro_label |
| 699 | 4 | Katalina Oviedo | elif parametro.lower() == "ph": |
| 700 | 4 | Katalina Oviedo | self.ph_label = valor_label |
| 701 | 4 | Katalina Oviedo | self.parametro_ph_label = parametro_label |
| 702 | 4 | Katalina Oviedo | elif parametro.lower() == "luz": |
| 703 | 4 | Katalina Oviedo | self.luz_label = valor_label |
| 704 | 4 | Katalina Oviedo | self.parametro_luz_label = parametro_label |
| 705 | 4 | Katalina Oviedo | |
| 706 | 4 | Katalina Oviedo | return valor_label |
| 707 | 4 | Katalina Oviedo | |
| 708 | 4 | Katalina Oviedo | def cargar_estilos(self): |
| 709 | 4 | Katalina Oviedo | with open("styles.css", "r") as f: |
| 710 | 4 | Katalina Oviedo | css = f.read() |
| 711 | 4 | Katalina Oviedo | self.setStyleSheet(css) |
| 712 | 4 | Katalina Oviedo | |
| 713 | 4 | Katalina Oviedo | def enviar_datos(self): |
| 714 | 4 | Katalina Oviedo | """Recopila los datos ingresados en los inputs y los guarda.""" |
| 715 | 4 | Katalina Oviedo | datos = [input_widget.text() for input_widget in self.inputs] |
| 716 | 4 | Katalina Oviedo | |
| 717 | 4 | Katalina Oviedo | # Guardamos los rangos |
| 718 | 4 | Katalina Oviedo | self.rangos = { |
| 719 | 4 | Katalina Oviedo | 'temperatura': {'min': float(datos[0]), 'max': float(datos[1])}, |
| 720 | 4 | Katalina Oviedo | 'ph': {'min': float(datos[2]), 'max': float(datos[3])}, |
| 721 | 4 | Katalina Oviedo | 'luz': {'min': float(datos[4]), 'max': float(datos[5])}, |
| 722 | 4 | Katalina Oviedo | } |
| 723 | 4 | Katalina Oviedo | |
| 724 | 4 | Katalina Oviedo | app = QApplication(sys.argv) |
| 725 | 4 | Katalina Oviedo | ventana = AquaPiApp() |
| 726 | 4 | Katalina Oviedo | ventana.show() |
| 727 | 4 | Katalina Oviedo | sys.exit(app.exec()) |
| 728 | 4 | Katalina Oviedo | </code></pre> |
| 729 | 4 | Katalina Oviedo | }} |
| 730 | 5 | Katalina Oviedo | |
| 731 | 5 | Katalina Oviedo | {{collapse(Código servidor) |
| 732 | 5 | Katalina Oviedo | |
| 733 | 5 | Katalina Oviedo | </code></pre> |
| 734 | 5 | Katalina Oviedo | }} |