Código e implementación » History » Version 4
Katalina Oviedo, 12/10/2024 04:19 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 | }} |