feat: split ui in multiple files for maintainability
All checks were successful
/ mirror (push) Successful in 4s
All checks were successful
/ mirror (push) Successful in 4s
This commit is contained in:
parent
225b40b34f
commit
bf6ac57f51
@ -1,122 +1,14 @@
|
||||
import sys
|
||||
import random
|
||||
import socket
|
||||
from PyQt5.QtWidgets import (
|
||||
QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
|
||||
QPushButton, QLabel, QFrame, QSizePolicy, QStyle
|
||||
)
|
||||
from PyQt5.QtCore import Qt, QTimer, QSize, QPointF, pyqtSignal, QThread
|
||||
from PyQt5.QtGui import QPainter, QColor, QPen, QLinearGradient, QBrush, QIcon, QFont
|
||||
|
||||
# --- Phone Client Thread ---
|
||||
class PhoneClient(QThread):
|
||||
data_received = pyqtSignal(bytes, int) # Include client_id
|
||||
state_changed = pyqtSignal(str, str, int) # Include client_id
|
||||
|
||||
def __init__(self, host, port, client_id):
|
||||
super().__init__()
|
||||
self.host = host
|
||||
self.port = port
|
||||
self.client_id = client_id
|
||||
self.sock = None
|
||||
self.running = True
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
|
||||
self.sock.settimeout(15)
|
||||
self.sock.connect((self.host, self.port))
|
||||
print(f"Client {self.client_id} connected to {self.host}:{self.port}")
|
||||
while self.running:
|
||||
try:
|
||||
data = self.sock.recv(1024)
|
||||
if not data:
|
||||
print(f"Client {self.client_id} disconnected")
|
||||
self.state_changed.emit("CALL_END", "", self.client_id)
|
||||
break
|
||||
decoded_data = data.decode('utf-8', errors='ignore').strip()
|
||||
print(f"Client {self.client_id} received raw: {decoded_data}")
|
||||
if decoded_data in ["RINGING", "CALL_END", "CALL_DROPPED", "IN_CALL"]:
|
||||
self.state_changed.emit(decoded_data, "", self.client_id)
|
||||
else:
|
||||
self.data_received.emit(data, self.client_id)
|
||||
print(f"Client {self.client_id} received audio: {decoded_data}")
|
||||
except socket.timeout:
|
||||
print(f"Client {self.client_id} timed out waiting for data")
|
||||
continue
|
||||
except Exception as e:
|
||||
print(f"Client {self.client_id} error: {e}")
|
||||
self.state_changed.emit("CALL_END", "", self.client_id)
|
||||
break
|
||||
except Exception as e:
|
||||
print(f"Client {self.client_id} connection failed: {e}")
|
||||
finally:
|
||||
if self.sock:
|
||||
self.sock.close()
|
||||
|
||||
def send(self, message):
|
||||
if self.sock and self.running:
|
||||
try:
|
||||
self.sock.send(message.encode())
|
||||
print(f"Client {self.client_id} sent: {message}")
|
||||
except Exception as e:
|
||||
print(f"Client {self.client_id} send error: {e}")
|
||||
|
||||
def stop(self):
|
||||
self.running = False
|
||||
if self.sock:
|
||||
self.sock.close()
|
||||
|
||||
# --- Custom Waveform Widget ---
|
||||
class WaveformWidget(QWidget):
|
||||
def __init__(self, parent=None, dynamic=False):
|
||||
super().__init__(parent)
|
||||
self.dynamic = dynamic
|
||||
self.setMinimumSize(200, 80)
|
||||
self.setMaximumHeight(100)
|
||||
self.waveform_data = [random.randint(10, 90) for _ in range(50)]
|
||||
if self.dynamic:
|
||||
self.timer = QTimer(self)
|
||||
self.timer.timeout.connect(self.update_waveform)
|
||||
self.timer.start(100)
|
||||
|
||||
def update_waveform(self):
|
||||
self.waveform_data = self.waveform_data[1:] + [random.randint(10, 90)]
|
||||
self.update()
|
||||
|
||||
def set_data(self, data):
|
||||
amplitude = sum(byte for byte in data) % 90 + 10
|
||||
self.waveform_data = self.waveform_data[1:] + [amplitude]
|
||||
self.update()
|
||||
|
||||
def paintEvent(self, event):
|
||||
painter = QPainter(self)
|
||||
painter.setRenderHint(QPainter.Antialiasing)
|
||||
painter.fillRect(self.rect(), QColor("#2D2D2D"))
|
||||
gradient = QLinearGradient(0, 0, 0, self.height())
|
||||
gradient.setColorAt(0.0, QColor("#0078D4"))
|
||||
gradient.setColorAt(1.0, QColor("#50E6A4"))
|
||||
pen = QPen(QBrush(gradient), 2)
|
||||
painter.setPen(pen)
|
||||
bar_width = self.width() / len(self.waveform_data)
|
||||
max_h = self.height() - 10
|
||||
for i, val in enumerate(self.waveform_data):
|
||||
bar_height = (val / 100.0) * max_h
|
||||
x = i * bar_width
|
||||
y = (self.height() - bar_height) / 2
|
||||
painter.drawLine(QPointF(x + bar_width / 2, y), QPointF(x + bar_width / 2, y + bar_height))
|
||||
|
||||
def resizeEvent(self, event):
|
||||
super().resizeEvent(event)
|
||||
self.update()
|
||||
|
||||
# --- Phone State ---
|
||||
class PhoneState:
|
||||
IDLE = 0
|
||||
CALLING = 1
|
||||
IN_CALL = 2
|
||||
RINGING = 3
|
||||
from PyQt5.QtCore import Qt, QTimer, QSize
|
||||
from PyQt5.QtGui import QFont
|
||||
from phone_client import PhoneClient
|
||||
from waveform_widget import WaveformWidget
|
||||
from phone_state import PhoneState
|
||||
|
||||
class PhoneUI(QMainWindow):
|
||||
def __init__(self):
|
||||
@ -182,7 +74,6 @@ class PhoneUI(QMainWindow):
|
||||
client.state_changed.connect(lambda state, num, cid=i: self.set_phone_state(cid, self.map_state(state), num))
|
||||
client.start()
|
||||
|
||||
# Corrected lambda to handle 'checked' and capture phone_id
|
||||
phone_widget_container, phone_display, phone_button, phone_waveform, phone_status_label = self._create_phone_ui(
|
||||
f"Phone {i+1}", lambda checked, phone_id=i: self.phone_action(phone_id)
|
||||
)
|
61
protocol_prototype/DryBox/UI/phone_client.py
Normal file
61
protocol_prototype/DryBox/UI/phone_client.py
Normal file
@ -0,0 +1,61 @@
|
||||
import socket
|
||||
from PyQt5.QtCore import QThread, pyqtSignal
|
||||
|
||||
class PhoneClient(QThread):
|
||||
data_received = pyqtSignal(bytes, int) # Include client_id
|
||||
state_changed = pyqtSignal(str, str, int) # Include client_id
|
||||
|
||||
def __init__(self, host, port, client_id):
|
||||
super().__init__()
|
||||
self.host = host
|
||||
self.port = port
|
||||
self.client_id = client_id
|
||||
self.sock = None
|
||||
self.running = True
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
|
||||
self.sock.settimeout(15)
|
||||
self.sock.connect((self.host, self.port))
|
||||
print(f"Client {self.client_id} connected to {self.host}:{self.port}")
|
||||
while self.running:
|
||||
try:
|
||||
data = self.sock.recv(1024)
|
||||
if not data:
|
||||
print(f"Client {self.client_id} disconnected")
|
||||
self.state_changed.emit("CALL_END", "", self.client_id)
|
||||
break
|
||||
decoded_data = data.decode('utf-8', errors='ignore').strip()
|
||||
print(f"Client {self.client_id} received raw: {decoded_data}")
|
||||
if decoded_data in ["RINGING", "CALL_END", "CALL_DROPPED", "IN_CALL"]:
|
||||
self.state_changed.emit(decoded_data, "", self.client_id)
|
||||
else:
|
||||
self.data_received.emit(data, self.client_id)
|
||||
print(f"Client {self.client_id} received audio: {decoded_data}")
|
||||
except socket.timeout:
|
||||
print(f"Client {self.client_id} timed out waiting for data")
|
||||
continue
|
||||
except Exception as e:
|
||||
print(f"Client {self.client_id} error: {e}")
|
||||
self.state_changed.emit("CALL_END", "", self.client_id)
|
||||
break
|
||||
except Exception as e:
|
||||
print(f"Client {self.client_id} connection failed: {e}")
|
||||
finally:
|
||||
if self.sock:
|
||||
self.sock.close()
|
||||
|
||||
def send(self, message):
|
||||
if self.sock and self.running:
|
||||
try:
|
||||
self.sock.send(message.encode())
|
||||
print(f"Client {self.client_id} sent: {message}")
|
||||
except Exception as e:
|
||||
print(f"Client {self.client_id} send error: {e}")
|
||||
|
||||
def stop(self):
|
||||
self.running = False
|
||||
if self.sock:
|
||||
self.sock.close()
|
5
protocol_prototype/DryBox/UI/phone_state.py
Normal file
5
protocol_prototype/DryBox/UI/phone_state.py
Normal file
@ -0,0 +1,5 @@
|
||||
class PhoneState:
|
||||
IDLE = 0
|
||||
CALLING = 1
|
||||
IN_CALL = 2
|
||||
RINGING = 3
|
46
protocol_prototype/DryBox/UI/waveform_widget.py
Normal file
46
protocol_prototype/DryBox/UI/waveform_widget.py
Normal file
@ -0,0 +1,46 @@
|
||||
import random
|
||||
from PyQt5.QtWidgets import QWidget
|
||||
from PyQt5.QtCore import QTimer, QSize, QPointF
|
||||
from PyQt5.QtGui import QPainter, QColor, QPen, QLinearGradient, QBrush
|
||||
|
||||
class WaveformWidget(QWidget):
|
||||
def __init__(self, parent=None, dynamic=False):
|
||||
super().__init__(parent)
|
||||
self.dynamic = dynamic
|
||||
self.setMinimumSize(200, 80)
|
||||
self.setMaximumHeight(100)
|
||||
self.waveform_data = [random.randint(10, 90) for _ in range(50)]
|
||||
if self.dynamic:
|
||||
self.timer = QTimer(self)
|
||||
self.timer.timeout.connect(self.update_waveform)
|
||||
self.timer.start(100)
|
||||
|
||||
def update_waveform(self):
|
||||
self.waveform_data = self.waveform_data[1:] + [random.randint(10, 90)]
|
||||
self.update()
|
||||
|
||||
def set_data(self, data):
|
||||
amplitude = sum(byte for byte in data) % 90 + 10
|
||||
self.waveform_data = self.waveform_data[1:] + [amplitude]
|
||||
self.update()
|
||||
|
||||
def paintEvent(self, event):
|
||||
painter = QPainter(self)
|
||||
painter.setRenderHint(QPainter.Antialiasing)
|
||||
painter.fillRect(self.rect(), QColor("#2D2D2D"))
|
||||
gradient = QLinearGradient(0, 0, 0, self.height())
|
||||
gradient.setColorAt(0.0, QColor("#0078D4"))
|
||||
gradient.setColorAt(1.0, QColor("#50E6A4"))
|
||||
pen = QPen(QBrush(gradient), 2)
|
||||
painter.setPen(pen)
|
||||
bar_width = self.width() / len(self.waveform_data)
|
||||
max_h = self.height() - 10
|
||||
for i, val in enumerate(self.waveform_data):
|
||||
bar_height = (val / 100.0) * max_h
|
||||
x = i * bar_width
|
||||
y = (self.height() - bar_height) / 2
|
||||
painter.drawLine(QPointF(x + bar_width / 2, y), QPointF(x + bar_width / 2, y + bar_height))
|
||||
|
||||
def resizeEvent(self, event):
|
||||
super().resizeEvent(event)
|
||||
self.update()
|
Loading…
Reference in New Issue
Block a user