This commit is contained in:
parent
1d5eae7d80
commit
4cc9e8b2d2
330
protocol_prototype/DryBox/UI/gsm_status_widget.py
Normal file
330
protocol_prototype/DryBox/UI/gsm_status_widget.py
Normal file
@ -0,0 +1,330 @@
|
||||
from PyQt5.QtWidgets import (
|
||||
QWidget, QVBoxLayout, QHBoxLayout, QLabel, QFrame,
|
||||
QGridLayout, QProgressBar
|
||||
)
|
||||
from PyQt5.QtCore import Qt, pyqtSignal, QTimer
|
||||
from PyQt5.QtGui import QFont, QPalette, QColor, QPainter, QBrush, QLinearGradient
|
||||
import math
|
||||
|
||||
class SignalStrengthWidget(QWidget):
|
||||
"""Custom widget to display signal strength bars"""
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.signal_strength = -70 # dBm
|
||||
self.setMinimumSize(60, 40)
|
||||
self.setMaximumSize(80, 50)
|
||||
|
||||
def set_signal_strength(self, dbm):
|
||||
self.signal_strength = dbm
|
||||
self.update()
|
||||
|
||||
def paintEvent(self, event):
|
||||
painter = QPainter(self)
|
||||
painter.setRenderHint(QPainter.Antialiasing)
|
||||
|
||||
# Calculate number of bars based on signal strength
|
||||
# -50 dBm or better = 5 bars
|
||||
# -60 dBm = 4 bars
|
||||
# -70 dBm = 3 bars
|
||||
# -80 dBm = 2 bars
|
||||
# -90 dBm = 1 bar
|
||||
# < -90 dBm = 0 bars
|
||||
|
||||
if self.signal_strength >= -50:
|
||||
active_bars = 5
|
||||
elif self.signal_strength >= -60:
|
||||
active_bars = 4
|
||||
elif self.signal_strength >= -70:
|
||||
active_bars = 3
|
||||
elif self.signal_strength >= -80:
|
||||
active_bars = 2
|
||||
elif self.signal_strength >= -90:
|
||||
active_bars = 1
|
||||
else:
|
||||
active_bars = 0
|
||||
|
||||
bar_width = 10
|
||||
bar_spacing = 3
|
||||
max_height = 35
|
||||
|
||||
for i in range(5):
|
||||
x = i * (bar_width + bar_spacing) + 5
|
||||
bar_height = (i + 1) * 7
|
||||
y = max_height - bar_height + 10
|
||||
|
||||
if i < active_bars:
|
||||
# Active bar - gradient from green to yellow to red based on strength
|
||||
if self.signal_strength >= -60:
|
||||
color = QColor(76, 175, 80) # Green
|
||||
elif self.signal_strength >= -75:
|
||||
color = QColor(255, 193, 7) # Amber
|
||||
else:
|
||||
color = QColor(244, 67, 54) # Red
|
||||
else:
|
||||
# Inactive bar
|
||||
color = QColor(60, 60, 60)
|
||||
|
||||
painter.fillRect(x, y, bar_width, bar_height, color)
|
||||
|
||||
class GSMStatusWidget(QFrame):
|
||||
"""Widget to display GSM network status and parameters"""
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setObjectName("gsmStatusWidget")
|
||||
self.setFrameStyle(QFrame.StyledPanel)
|
||||
|
||||
# Default settings
|
||||
self.settings = {
|
||||
'signal_strength': -70,
|
||||
'signal_quality': 75,
|
||||
'noise_level': 10,
|
||||
'codec_mode': 'AMR-NB',
|
||||
'bitrate': 12.2,
|
||||
'packet_loss': 0,
|
||||
'jitter': 20,
|
||||
'latency': 100,
|
||||
'fading_enabled': False,
|
||||
'fading_speed': 'slow',
|
||||
'interference_enabled': False,
|
||||
'handover_enabled': False
|
||||
}
|
||||
|
||||
self.init_ui()
|
||||
self.update_display()
|
||||
|
||||
def init_ui(self):
|
||||
"""Initialize the UI components"""
|
||||
main_layout = QVBoxLayout()
|
||||
main_layout.setSpacing(10)
|
||||
main_layout.setContentsMargins(15, 15, 15, 15)
|
||||
|
||||
# Title
|
||||
title_label = QLabel("📡 GSM Network Status")
|
||||
title_label.setObjectName("gsmStatusTitle")
|
||||
title_label.setAlignment(Qt.AlignCenter)
|
||||
main_layout.addWidget(title_label)
|
||||
|
||||
# Main status grid
|
||||
status_grid = QGridLayout()
|
||||
status_grid.setSpacing(12)
|
||||
|
||||
# Row 0: Signal strength visualization
|
||||
status_grid.addWidget(QLabel("Signal:"), 0, 0)
|
||||
self.signal_widget = SignalStrengthWidget()
|
||||
status_grid.addWidget(self.signal_widget, 0, 1)
|
||||
self.signal_dbm_label = QLabel("-70 dBm")
|
||||
self.signal_dbm_label.setObjectName("signalDbmLabel")
|
||||
status_grid.addWidget(self.signal_dbm_label, 0, 2)
|
||||
|
||||
# Row 1: Signal quality bar
|
||||
status_grid.addWidget(QLabel("Quality:"), 1, 0)
|
||||
self.quality_bar = QProgressBar()
|
||||
self.quality_bar.setTextVisible(True)
|
||||
self.quality_bar.setRange(0, 100)
|
||||
self.quality_bar.setObjectName("qualityBar")
|
||||
status_grid.addWidget(self.quality_bar, 1, 1, 1, 2)
|
||||
|
||||
# Row 2: Noise level
|
||||
status_grid.addWidget(QLabel("Noise:"), 2, 0)
|
||||
self.noise_bar = QProgressBar()
|
||||
self.noise_bar.setTextVisible(True)
|
||||
self.noise_bar.setRange(0, 50)
|
||||
self.noise_bar.setObjectName("noiseBar")
|
||||
status_grid.addWidget(self.noise_bar, 2, 1, 1, 2)
|
||||
|
||||
main_layout.addLayout(status_grid)
|
||||
|
||||
# Separator
|
||||
separator1 = QFrame()
|
||||
separator1.setFrameShape(QFrame.HLine)
|
||||
separator1.setObjectName("gsmSeparator")
|
||||
main_layout.addWidget(separator1)
|
||||
|
||||
# Codec info section
|
||||
codec_layout = QHBoxLayout()
|
||||
codec_layout.setSpacing(15)
|
||||
|
||||
codec_icon = QLabel("🎤")
|
||||
codec_layout.addWidget(codec_icon)
|
||||
|
||||
self.codec_label = QLabel("AMR-NB @ 12.2 kbps")
|
||||
self.codec_label.setObjectName("codecLabel")
|
||||
codec_layout.addWidget(self.codec_label)
|
||||
codec_layout.addStretch()
|
||||
|
||||
main_layout.addLayout(codec_layout)
|
||||
|
||||
# Network conditions section
|
||||
network_grid = QGridLayout()
|
||||
network_grid.setSpacing(8)
|
||||
|
||||
# Packet loss indicator
|
||||
network_grid.addWidget(QLabel("📉"), 0, 0)
|
||||
self.packet_loss_label = QLabel("Loss: 0%")
|
||||
self.packet_loss_label.setObjectName("networkParam")
|
||||
network_grid.addWidget(self.packet_loss_label, 0, 1)
|
||||
|
||||
# Jitter indicator
|
||||
network_grid.addWidget(QLabel("📊"), 0, 2)
|
||||
self.jitter_label = QLabel("Jitter: 20ms")
|
||||
self.jitter_label.setObjectName("networkParam")
|
||||
network_grid.addWidget(self.jitter_label, 0, 3)
|
||||
|
||||
# Latency indicator
|
||||
network_grid.addWidget(QLabel("⏱"), 1, 0)
|
||||
self.latency_label = QLabel("Latency: 100ms")
|
||||
self.latency_label.setObjectName("networkParam")
|
||||
network_grid.addWidget(self.latency_label, 1, 1)
|
||||
|
||||
# Features status
|
||||
network_grid.addWidget(QLabel("🌊"), 1, 2)
|
||||
self.features_label = QLabel("Standard")
|
||||
self.features_label.setObjectName("networkParam")
|
||||
network_grid.addWidget(self.features_label, 1, 3)
|
||||
|
||||
main_layout.addLayout(network_grid)
|
||||
|
||||
# Connection status
|
||||
separator2 = QFrame()
|
||||
separator2.setFrameShape(QFrame.HLine)
|
||||
separator2.setObjectName("gsmSeparator")
|
||||
main_layout.addWidget(separator2)
|
||||
|
||||
self.connection_status = QLabel("🟢 Connected to GSM Network")
|
||||
self.connection_status.setObjectName("connectionStatus")
|
||||
self.connection_status.setAlignment(Qt.AlignCenter)
|
||||
main_layout.addWidget(self.connection_status)
|
||||
|
||||
main_layout.addStretch()
|
||||
self.setLayout(main_layout)
|
||||
|
||||
# Apply styling
|
||||
self.setStyleSheet("""
|
||||
#gsmStatusWidget {
|
||||
background-color: #2A2A2A;
|
||||
border: 2px solid #0078D4;
|
||||
border-radius: 10px;
|
||||
}
|
||||
#gsmStatusTitle {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #00A2E8;
|
||||
padding: 5px;
|
||||
}
|
||||
#signalDbmLabel {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
color: #4CAF50;
|
||||
}
|
||||
#qualityBar {
|
||||
background-color: #1E1E1E;
|
||||
border: 1px solid #444;
|
||||
border-radius: 3px;
|
||||
text-align: center;
|
||||
color: white;
|
||||
}
|
||||
#qualityBar::chunk {
|
||||
background-color: qlineargradient(x1:0, y1:0, x2:1, y2:0,
|
||||
stop:0 #4CAF50, stop:0.5 #8BC34A, stop:1 #CDDC39);
|
||||
border-radius: 3px;
|
||||
}
|
||||
#noiseBar {
|
||||
background-color: #1E1E1E;
|
||||
border: 1px solid #444;
|
||||
border-radius: 3px;
|
||||
text-align: center;
|
||||
color: white;
|
||||
}
|
||||
#noiseBar::chunk {
|
||||
background-color: qlineargradient(x1:0, y1:0, x2:1, y2:0,
|
||||
stop:0 #FFC107, stop:0.5 #FF9800, stop:1 #FF5722);
|
||||
border-radius: 3px;
|
||||
}
|
||||
#codecLabel {
|
||||
font-size: 13px;
|
||||
color: #E0E0E0;
|
||||
font-weight: bold;
|
||||
}
|
||||
#networkParam {
|
||||
font-size: 12px;
|
||||
color: #B0B0B0;
|
||||
}
|
||||
#gsmSeparator {
|
||||
background-color: #444;
|
||||
max-height: 1px;
|
||||
margin: 5px 0;
|
||||
}
|
||||
#connectionStatus {
|
||||
font-size: 13px;
|
||||
color: #4CAF50;
|
||||
font-weight: bold;
|
||||
padding: 5px;
|
||||
}
|
||||
QLabel {
|
||||
color: #E0E0E0;
|
||||
}
|
||||
""")
|
||||
|
||||
def update_settings(self, settings):
|
||||
"""Update the displayed settings"""
|
||||
self.settings = settings
|
||||
self.update_display()
|
||||
|
||||
def update_display(self):
|
||||
"""Update all display elements based on current settings"""
|
||||
# Update signal strength
|
||||
self.signal_widget.set_signal_strength(self.settings['signal_strength'])
|
||||
self.signal_dbm_label.setText(f"{self.settings['signal_strength']} dBm")
|
||||
|
||||
# Color code the dBm label
|
||||
if self.settings['signal_strength'] >= -60:
|
||||
self.signal_dbm_label.setStyleSheet("color: #4CAF50;") # Green
|
||||
elif self.settings['signal_strength'] >= -75:
|
||||
self.signal_dbm_label.setStyleSheet("color: #FFC107;") # Amber
|
||||
else:
|
||||
self.signal_dbm_label.setStyleSheet("color: #FF5722;") # Red
|
||||
|
||||
# Update quality bar
|
||||
self.quality_bar.setValue(self.settings['signal_quality'])
|
||||
self.quality_bar.setFormat(f"{self.settings['signal_quality']}%")
|
||||
|
||||
# Update noise bar
|
||||
self.noise_bar.setValue(self.settings['noise_level'])
|
||||
self.noise_bar.setFormat(f"{self.settings['noise_level']}%")
|
||||
|
||||
# Update codec info
|
||||
self.codec_label.setText(f"{self.settings['codec_mode']} @ {self.settings['bitrate']} kbps")
|
||||
|
||||
# Update network parameters
|
||||
self.packet_loss_label.setText(f"Loss: {self.settings['packet_loss']}%")
|
||||
self.jitter_label.setText(f"Jitter: {self.settings['jitter']}ms")
|
||||
self.latency_label.setText(f"Latency: {self.settings['latency']}ms")
|
||||
|
||||
# Update features
|
||||
features = []
|
||||
if self.settings['fading_enabled']:
|
||||
features.append(f"Fading({self.settings['fading_speed']})")
|
||||
if self.settings['interference_enabled']:
|
||||
features.append("Interference")
|
||||
if self.settings['handover_enabled']:
|
||||
features.append("Handover")
|
||||
|
||||
if features:
|
||||
self.features_label.setText(", ".join(features))
|
||||
else:
|
||||
self.features_label.setText("Standard")
|
||||
|
||||
# Update connection status based on signal quality
|
||||
if self.settings['signal_quality'] >= 80:
|
||||
self.connection_status.setText("🟢 Excellent Connection")
|
||||
self.connection_status.setStyleSheet("color: #4CAF50;")
|
||||
elif self.settings['signal_quality'] >= 60:
|
||||
self.connection_status.setText("🟡 Good Connection")
|
||||
self.connection_status.setStyleSheet("color: #FFC107;")
|
||||
elif self.settings['signal_quality'] >= 40:
|
||||
self.connection_status.setText("🟠 Fair Connection")
|
||||
self.connection_status.setStyleSheet("color: #FF9800;")
|
||||
else:
|
||||
self.connection_status.setText("🔴 Poor Connection")
|
||||
self.connection_status.setStyleSheet("color: #FF5722;")
|
@ -11,6 +11,8 @@ import threading
|
||||
from phone_manager import PhoneManager
|
||||
from waveform_widget import WaveformWidget
|
||||
from phone_state import PhoneState
|
||||
from gsm_settings_dialog import GSMSettingsDialog
|
||||
from gsm_status_widget import GSMStatusWidget
|
||||
|
||||
class PhoneUI(QMainWindow):
|
||||
debug_signal = pyqtSignal(str)
|
||||
@ -27,45 +29,106 @@ class PhoneUI(QMainWindow):
|
||||
self.auto_test_running = False
|
||||
self.auto_test_timer = None
|
||||
self.test_step = 0
|
||||
|
||||
# GSM settings dialog (will be created on demand)
|
||||
self.gsm_settings_dialog = None
|
||||
self.gsm_settings = None
|
||||
|
||||
# GSM simulation timer
|
||||
self.gsm_simulation_timer = None
|
||||
self.setStyleSheet("""
|
||||
QMainWindow { background-color: #333333; }
|
||||
QLabel { color: #E0E0E0; font-size: 14px; }
|
||||
QPushButton {
|
||||
background-color: #0078D4; color: white; border: none;
|
||||
padding: 10px 15px; border-radius: 5px; font-size: 14px;
|
||||
min-height: 30px;
|
||||
QMainWindow {
|
||||
background-color: #1a1a1a;
|
||||
}
|
||||
QLabel {
|
||||
color: #E0E0E0;
|
||||
font-size: 14px;
|
||||
}
|
||||
QPushButton {
|
||||
background-color: #0078D4;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 10px 15px;
|
||||
border-radius: 5px;
|
||||
font-size: 14px;
|
||||
min-height: 30px;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #005A9E;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #003C6B;
|
||||
}
|
||||
QPushButton#settingsButton {
|
||||
background-color: #555555;
|
||||
}
|
||||
QPushButton#settingsButton:hover {
|
||||
background-color: #777777;
|
||||
}
|
||||
QPushButton:hover { background-color: #005A9E; }
|
||||
QPushButton:pressed { background-color: #003C6B; }
|
||||
QPushButton#settingsButton { background-color: #555555; }
|
||||
QPushButton#settingsButton:hover { background-color: #777777; }
|
||||
QFrame#phoneDisplay {
|
||||
background-color: #1E1E1E; border: 2px solid #0078D4;
|
||||
border-radius: 10px;
|
||||
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
|
||||
stop:0 #2A2A2A, stop:1 #1E1E1E);
|
||||
border: 2px solid #0078D4;
|
||||
border-radius: 12px;
|
||||
}
|
||||
QLabel#phoneTitleLabel {
|
||||
font-size: 18px; font-weight: bold; padding-bottom: 5px;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
padding: 10px;
|
||||
color: #FFFFFF;
|
||||
background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
|
||||
stop:0 #0078D4, stop:0.5 #00A2E8, stop:1 #0078D4);
|
||||
border-radius: 8px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
QLabel#mainTitleLabel {
|
||||
font-size: 24px; font-weight: bold; color: #00A2E8;
|
||||
padding: 15px;
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
color: #00A2E8;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
QWidget#phoneWidget {
|
||||
border: 2px solid #4A4A4A; border-radius: 10px;
|
||||
background-color: #3A3A3A;
|
||||
border: 2px solid #0078D4;
|
||||
border-radius: 15px;
|
||||
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
|
||||
stop:0 #3A3A3A, stop:0.5 #2F2F2F, stop:1 #252525);
|
||||
min-width: 400px;
|
||||
padding: 10px;
|
||||
padding: 15px;
|
||||
margin: 5px;
|
||||
}
|
||||
QWidget#phoneWidget:hover {
|
||||
border: 2px solid #00A2E8;
|
||||
}
|
||||
QTextEdit#debugConsole {
|
||||
background-color: #1E1E1E; color: #00FF00;
|
||||
font-family: monospace; font-size: 12px;
|
||||
border: 2px solid #0078D4; border-radius: 5px;
|
||||
background-color: #0a0a0a;
|
||||
color: #00FF00;
|
||||
font-family: 'Consolas', 'Monaco', monospace;
|
||||
font-size: 12px;
|
||||
border: 2px solid #0078D4;
|
||||
border-radius: 8px;
|
||||
padding: 10px;
|
||||
}
|
||||
QPushButton#autoTestButton {
|
||||
background-color: #FF8C00; min-height: 35px;
|
||||
background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
|
||||
stop:0 #FF8C00, stop:1 #FFA500);
|
||||
min-height: 35px;
|
||||
font-size: 15px;
|
||||
}
|
||||
QPushButton#autoTestButton:hover {
|
||||
background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
|
||||
stop:0 #FF7F00, stop:1 #FF9500);
|
||||
}
|
||||
QSplitter::handle {
|
||||
background-color: #0078D4;
|
||||
width: 3px;
|
||||
height: 3px;
|
||||
}
|
||||
QSplitter::handle:hover {
|
||||
background-color: #00A2E8;
|
||||
}
|
||||
QPushButton#autoTestButton:hover { background-color: #FF7F00; }
|
||||
""")
|
||||
|
||||
# Setup debug signal early
|
||||
@ -81,9 +144,19 @@ class PhoneUI(QMainWindow):
|
||||
main_layout = QVBoxLayout()
|
||||
main_widget.setLayout(main_layout)
|
||||
|
||||
# Create splitter for phones and debug console
|
||||
# Create main horizontal splitter for GSM status and phones/debug
|
||||
self.main_h_splitter = QSplitter(Qt.Horizontal)
|
||||
main_layout.addWidget(self.main_h_splitter)
|
||||
|
||||
# GSM Status Panel (left side)
|
||||
self.gsm_status_widget = GSMStatusWidget()
|
||||
self.gsm_status_widget.setMinimumWidth(280)
|
||||
self.gsm_status_widget.setMaximumWidth(350)
|
||||
self.main_h_splitter.addWidget(self.gsm_status_widget)
|
||||
|
||||
# Create vertical splitter for phones and debug console (right side)
|
||||
self.splitter = QSplitter(Qt.Vertical)
|
||||
main_layout.addWidget(self.splitter)
|
||||
self.main_h_splitter.addWidget(self.splitter)
|
||||
|
||||
# Top widget for phones
|
||||
phones_widget = QWidget()
|
||||
@ -188,8 +261,9 @@ class PhoneUI(QMainWindow):
|
||||
self.debug_console.append(msg)
|
||||
del self._debug_queue
|
||||
|
||||
# Set splitter sizes (70% phones, 30% debug)
|
||||
self.splitter.setSizes([600, 300])
|
||||
# Set splitter sizes
|
||||
self.splitter.setSizes([600, 300]) # 70% phones, 30% debug
|
||||
self.main_h_splitter.setSizes([300, 1100]) # GSM panel: 300px, rest: 1100px
|
||||
|
||||
# Initialize UI
|
||||
for phone in self.manager.phones:
|
||||
@ -343,25 +417,69 @@ class PhoneUI(QMainWindow):
|
||||
status_label = phone['status_label']
|
||||
|
||||
if state == PhoneState.IDLE:
|
||||
button.setText("Call")
|
||||
button.setText("📞 Call")
|
||||
button.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))
|
||||
status_label.setText("Idle")
|
||||
button.setStyleSheet("background-color: #0078D4;")
|
||||
status_label.setText("📱 Idle")
|
||||
status_label.setStyleSheet("font-size: 18px; color: #888888;")
|
||||
button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
|
||||
stop:0 #0078D4, stop:1 #005A9E);
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
|
||||
stop:0 #00A2E8, stop:1 #0078D4);
|
||||
}
|
||||
""")
|
||||
elif state == PhoneState.CALLING:
|
||||
button.setText("Cancel")
|
||||
button.setText("❌ Cancel")
|
||||
button.setIcon(self.style().standardIcon(QStyle.SP_MediaStop))
|
||||
status_label.setText(f"Calling {phone_number}...")
|
||||
button.setStyleSheet("background-color: #E81123;")
|
||||
status_label.setText(f"📲 Calling {phone_number}...")
|
||||
status_label.setStyleSheet("font-size: 18px; color: #FFC107;")
|
||||
button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
|
||||
stop:0 #E81123, stop:1 #B20F1F);
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
|
||||
stop:0 #FF1133, stop:1 #E81123);
|
||||
}
|
||||
""")
|
||||
elif state == PhoneState.IN_CALL:
|
||||
button.setText("Hang Up")
|
||||
button.setText("📵 Hang Up")
|
||||
button.setIcon(self.style().standardIcon(QStyle.SP_DialogCancelButton))
|
||||
status_label.setText(f"In Call with {phone_number}")
|
||||
button.setStyleSheet("background-color: #E81123;")
|
||||
status_label.setText(f"📞 In Call with {phone_number}")
|
||||
status_label.setStyleSheet("font-size: 18px; color: #4CAF50;")
|
||||
button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
|
||||
stop:0 #E81123, stop:1 #B20F1F);
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
|
||||
stop:0 #FF1133, stop:1 #E81123);
|
||||
}
|
||||
""")
|
||||
elif state == PhoneState.RINGING:
|
||||
button.setText("Answer")
|
||||
button.setText("✅ Answer")
|
||||
button.setIcon(self.style().standardIcon(QStyle.SP_DialogApplyButton))
|
||||
status_label.setText(f"Incoming Call from {phone_number}")
|
||||
button.setStyleSheet("background-color: #107C10;")
|
||||
status_label.setText(f"📱 Incoming Call from {phone_number}")
|
||||
status_label.setStyleSheet("font-size: 18px; color: #4CAF50; font-weight: bold;")
|
||||
button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
|
||||
stop:0 #107C10, stop:1 #0B5C0B);
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
|
||||
stop:0 #20AC20, stop:1 #107C10);
|
||||
}
|
||||
""")
|
||||
|
||||
def set_phone_state(self, client_id, state_str, number):
|
||||
self.debug(f"Phone {client_id + 1} state change: {state_str}")
|
||||
@ -370,18 +488,26 @@ class PhoneUI(QMainWindow):
|
||||
if state_str == "HANDSHAKE_COMPLETE":
|
||||
phone = self.manager.phones[client_id]
|
||||
phone['status_label'].setText("🔒 Secure Channel Established")
|
||||
phone['status_label'].setStyleSheet("font-size: 16px; color: #00A2E8; font-weight: bold;")
|
||||
self.debug(f"Phone {client_id + 1} secure channel established")
|
||||
self.manager.start_audio(client_id, parent=self)
|
||||
return
|
||||
elif state_str == "VOICE_START":
|
||||
phone = self.manager.phones[client_id]
|
||||
phone['status_label'].setText("🎤 Voice Active (Encrypted)")
|
||||
phone['status_label'].setStyleSheet("font-size: 16px; color: #4CAF50; font-weight: bold;")
|
||||
self.debug(f"Phone {client_id + 1} voice session started")
|
||||
# Start simulating real-time GSM parameter updates if we have settings
|
||||
if hasattr(self, 'gsm_settings') and self.gsm_settings:
|
||||
self.start_gsm_simulation()
|
||||
return
|
||||
elif state_str == "VOICE_END":
|
||||
phone = self.manager.phones[client_id]
|
||||
phone['status_label'].setText("🔒 Secure Channel")
|
||||
phone['status_label'].setStyleSheet("font-size: 16px; color: #00A2E8;")
|
||||
self.debug(f"Phone {client_id + 1} voice session ended")
|
||||
# Stop GSM simulation
|
||||
self.stop_gsm_simulation()
|
||||
return
|
||||
|
||||
# Handle regular states
|
||||
@ -409,8 +535,61 @@ class PhoneUI(QMainWindow):
|
||||
self.update_phone_ui(client_id)
|
||||
|
||||
def settings_action(self):
|
||||
print("Settings clicked")
|
||||
self.debug("Settings clicked")
|
||||
"""Show GSM settings dialog"""
|
||||
self.debug("Opening GSM settings dialog")
|
||||
|
||||
# Create dialog if not exists (lazy initialization)
|
||||
if self.gsm_settings_dialog is None:
|
||||
self.gsm_settings_dialog = GSMSettingsDialog(self)
|
||||
# Connect signal to handle settings changes
|
||||
self.gsm_settings_dialog.settings_changed.connect(self.on_gsm_settings_changed)
|
||||
|
||||
# Show dialog
|
||||
if self.gsm_settings_dialog.exec_():
|
||||
# User clicked OK, get the settings
|
||||
self.gsm_settings = self.gsm_settings_dialog.get_settings()
|
||||
self.debug("GSM settings updated:")
|
||||
self.debug(f" Signal strength: {self.gsm_settings['signal_strength']} dBm")
|
||||
self.debug(f" Signal quality: {self.gsm_settings['signal_quality']}%")
|
||||
self.debug(f" Noise level: {self.gsm_settings['noise_level']}%")
|
||||
self.debug(f" Codec: {self.gsm_settings['codec_mode']} @ {self.gsm_settings['bitrate']} kbps")
|
||||
self.debug(f" Network conditions - Packet loss: {self.gsm_settings['packet_loss']}%, Jitter: {self.gsm_settings['jitter']}ms, Latency: {self.gsm_settings['latency']}ms")
|
||||
|
||||
# Apply settings to phones if needed
|
||||
self.apply_gsm_settings()
|
||||
# Update GSM status widget
|
||||
self.gsm_status_widget.update_settings(self.gsm_settings)
|
||||
else:
|
||||
self.debug("GSM settings dialog cancelled")
|
||||
|
||||
def on_gsm_settings_changed(self, settings):
|
||||
"""Handle real-time settings changes if needed"""
|
||||
self.gsm_settings = settings
|
||||
self.debug("GSM settings changed (real-time update)")
|
||||
# Update GSM status widget in real-time
|
||||
self.gsm_status_widget.update_settings(settings)
|
||||
|
||||
def apply_gsm_settings(self):
|
||||
"""Apply GSM settings to the phone simulation"""
|
||||
if self.gsm_settings:
|
||||
# Here you can apply the settings to your phone manager or simulation
|
||||
# For example, update signal quality indicators, adjust codec parameters, etc.
|
||||
self.debug("Applying GSM settings to simulation...")
|
||||
|
||||
# Update UI to reflect signal conditions
|
||||
for phone_id, phone in enumerate(self.manager.phones):
|
||||
signal_quality = self.gsm_settings['signal_quality']
|
||||
if signal_quality >= 80:
|
||||
signal_icon = "📶" # Full signal
|
||||
elif signal_quality >= 60:
|
||||
signal_icon = "📶" # Good signal
|
||||
elif signal_quality >= 40:
|
||||
signal_icon = "📵" # Fair signal
|
||||
else:
|
||||
signal_icon = "📵" # Poor signal
|
||||
|
||||
# You can update status labels or other UI elements here
|
||||
self.debug(f"Phone {phone_id + 1} signal quality: {signal_quality}% {signal_icon}")
|
||||
|
||||
def debug(self, message):
|
||||
"""Thread-safe debug logging to both console and UI"""
|
||||
@ -714,6 +893,7 @@ class PhoneUI(QMainWindow):
|
||||
QShortcut(QKeySequence("Space"), self, self.toggle_auto_test)
|
||||
QShortcut(QKeySequence("Ctrl+L"), self, self.clear_debug)
|
||||
QShortcut(QKeySequence("Ctrl+A"), self, self.show_audio_menu)
|
||||
QShortcut(QKeySequence("Ctrl+G"), self, self.settings_action)
|
||||
|
||||
self.debug("Keyboard shortcuts enabled:")
|
||||
self.debug(" 1/2: Phone action (call/answer/hangup)")
|
||||
@ -722,15 +902,84 @@ class PhoneUI(QMainWindow):
|
||||
self.debug(" Space: Toggle auto test")
|
||||
self.debug(" Ctrl+L: Clear debug")
|
||||
self.debug(" Ctrl+A: Audio options menu")
|
||||
self.debug(" Ctrl+G: GSM settings")
|
||||
|
||||
def start_gsm_simulation(self):
|
||||
"""Start simulating GSM parameter changes during call"""
|
||||
if not self.gsm_simulation_timer:
|
||||
self.gsm_simulation_timer = QTimer()
|
||||
self.gsm_simulation_timer.timeout.connect(self.update_gsm_simulation)
|
||||
self.gsm_simulation_timer.start(1000) # Update every second
|
||||
self.debug("Started GSM parameter simulation")
|
||||
|
||||
def stop_gsm_simulation(self):
|
||||
"""Stop GSM parameter simulation"""
|
||||
if self.gsm_simulation_timer:
|
||||
self.gsm_simulation_timer.stop()
|
||||
self.gsm_simulation_timer = None
|
||||
self.debug("Stopped GSM parameter simulation")
|
||||
|
||||
def update_gsm_simulation(self):
|
||||
"""Simulate realistic GSM parameter variations during call"""
|
||||
if not hasattr(self, 'gsm_settings') or not self.gsm_settings:
|
||||
return
|
||||
|
||||
import random
|
||||
|
||||
# Simulate signal strength variation (±2 dBm)
|
||||
current_strength = self.gsm_settings['signal_strength']
|
||||
variation = random.uniform(-2, 2)
|
||||
new_strength = max(-120, min(-40, current_strength + variation))
|
||||
self.gsm_settings['signal_strength'] = int(new_strength)
|
||||
|
||||
# Simulate signal quality variation (±3%)
|
||||
current_quality = self.gsm_settings['signal_quality']
|
||||
quality_variation = random.uniform(-3, 3)
|
||||
new_quality = max(0, min(100, current_quality + quality_variation))
|
||||
self.gsm_settings['signal_quality'] = int(new_quality)
|
||||
|
||||
# Simulate noise level variation (±1%)
|
||||
current_noise = self.gsm_settings['noise_level']
|
||||
noise_variation = random.uniform(-1, 2)
|
||||
new_noise = max(0, min(50, current_noise + noise_variation))
|
||||
self.gsm_settings['noise_level'] = int(new_noise)
|
||||
|
||||
# Simulate packet loss variation (occasional spikes)
|
||||
if random.random() < 0.1: # 10% chance of packet loss spike
|
||||
self.gsm_settings['packet_loss'] = random.randint(1, 5)
|
||||
else:
|
||||
self.gsm_settings['packet_loss'] = 0
|
||||
|
||||
# Simulate jitter variation (±5ms)
|
||||
current_jitter = self.gsm_settings['jitter']
|
||||
jitter_variation = random.uniform(-5, 5)
|
||||
new_jitter = max(0, min(200, current_jitter + jitter_variation))
|
||||
self.gsm_settings['jitter'] = int(new_jitter)
|
||||
|
||||
# Update the GSM status widget
|
||||
self.gsm_status_widget.update_settings(self.gsm_settings)
|
||||
|
||||
def closeEvent(self, event):
|
||||
if self.auto_test_running:
|
||||
self.stop_auto_test()
|
||||
|
||||
# Stop GSM simulation
|
||||
self.stop_gsm_simulation()
|
||||
|
||||
# Clean up GSM settings dialog
|
||||
if self.gsm_settings_dialog is not None:
|
||||
self.gsm_settings_dialog.close()
|
||||
self.gsm_settings_dialog.deleteLater()
|
||||
self.gsm_settings_dialog = None
|
||||
|
||||
# Clean up audio player
|
||||
if hasattr(self.manager, 'audio_player'):
|
||||
self.manager.audio_player.cleanup()
|
||||
|
||||
# Stop all phone clients
|
||||
for phone in self.manager.phones:
|
||||
phone['client'].stop()
|
||||
|
||||
event.accept()
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
Loading…
Reference in New Issue
Block a user