Add theme support with dynamic monitoring and UI updates

This commit is contained in:
2025-07-31 14:50:21 +02:00
parent 9a44d9c059
commit 7aeaae8880
+360 -38
View File
@@ -1283,6 +1283,9 @@ class MainWindow(QtWidgets.QMainWindow):
self.selected_step_idx = None self.selected_step_idx = None
self.last_toggle_time = 0 # For debounce of start/stop self.last_toggle_time = 0 # For debounce of start/stop
# Theme support
self.theme_mode = 'system' # 'light', 'dark', or 'system'
# Memory optimization attributes # Memory optimization attributes
self._last_screenshot = None self._last_screenshot = None
self._last_screenshot_time = 0 self._last_screenshot_time = 0
@@ -1314,6 +1317,40 @@ class MainWindow(QtWidgets.QMainWindow):
# Initial resource display update (delayed to prevent startup hanging) # Initial resource display update (delayed to prevent startup hanging)
QtCore.QTimer.singleShot(2000, self._enable_resource_monitoring) QtCore.QTimer.singleShot(2000, self._enable_resource_monitoring)
# Setup system theme monitoring timer (only when in system mode)
self.theme_monitor_timer = QtCore.QTimer()
self.theme_monitor_timer.timeout.connect(self._check_system_theme_change)
self._last_system_theme = None
# Start theme monitoring if in system mode
if self.theme_mode == 'system':
self.theme_monitor_timer.start(5000) # Check every 5 seconds
def _check_system_theme_change(self):
"""
Check if the system theme has changed and update accordingly.
Only runs when theme_mode is 'system'.
"""
if self.theme_mode != 'system':
self.theme_monitor_timer.stop()
return
try:
current_system_theme = self.is_system_dark_theme()
if self._last_system_theme is None:
self._last_system_theme = current_system_theme
return
if current_system_theme != self._last_system_theme:
logger.info(f'System theme changed to: {"Dark" if current_system_theme else "Light"}')
self._last_system_theme = current_system_theme
self.apply_theme()
self._update_theme_button_text()
except Exception as e:
logger.debug(f"Error checking system theme change: {e}")
def _enable_resource_monitoring(self): def _enable_resource_monitoring(self):
"""Enable resource monitoring after startup delay.""" """Enable resource monitoring after startup delay."""
@@ -1406,16 +1443,18 @@ class MainWindow(QtWidgets.QMainWindow):
def _update_resource_display(self, memory_info): def _update_resource_display(self, memory_info):
""" """
Update the resource usage display in the UI. Update the resource usage display in the UI with theme-aware colors.
""" """
try: try:
colors = self.get_theme_styles()
# Memory usage # Memory usage
if memory_info.get('has_psutil', False): if memory_info.get('has_psutil', False):
memory_text = f"Memory: {memory_info['process_memory_mb']:.1f}MB ({memory_info['process_memory_percent']:.1f}%)" memory_text = f"Memory: {memory_info['process_memory_mb']:.1f}MB ({memory_info['process_memory_percent']:.1f}%)"
memory_color = '#d9534f' if memory_info['process_memory_percent'] > 10 else '#5cb85c' memory_color = colors['error'] if memory_info['process_memory_percent'] > 10 else colors['success']
else: else:
memory_text = "Memory: N/A (psutil needed)" memory_text = "Memory: N/A (psutil needed)"
memory_color = '#f0ad4e' memory_color = colors['warning']
self.memory_label.setText(memory_text) self.memory_label.setText(memory_text)
self.memory_label.setStyleSheet(f'font-size: 9pt; color: {memory_color};') self.memory_label.setStyleSheet(f'font-size: 9pt; color: {memory_color};')
@@ -1428,10 +1467,10 @@ class MainWindow(QtWidgets.QMainWindow):
# CPU usage # CPU usage
if memory_info.get('has_psutil', False): if memory_info.get('has_psutil', False):
cpu_text = f"CPU: {memory_info['cpu_percent']:.1f}%" cpu_text = f"CPU: {memory_info['cpu_percent']:.1f}%"
cpu_color = '#d9534f' if memory_info['cpu_percent'] > 50 else '#5cb85c' cpu_color = colors['error'] if memory_info['cpu_percent'] > 50 else colors['success']
else: else:
cpu_text = "CPU: N/A" cpu_text = "CPU: N/A"
cpu_color = '#777' cpu_color = colors['text_light']
self.cpu_label.setText(cpu_text) self.cpu_label.setText(cpu_text)
self.cpu_label.setStyleSheet(f'font-size: 9pt; color: {cpu_color};') self.cpu_label.setStyleSheet(f'font-size: 9pt; color: {cpu_color};')
@@ -1439,17 +1478,17 @@ class MainWindow(QtWidgets.QMainWindow):
# System memory # System memory
if memory_info.get('has_psutil', False): if memory_info.get('has_psutil', False):
system_text = f"System: {memory_info['system_memory_percent']:.1f}% ({memory_info['system_memory_available_gb']:.1f}GB free)" system_text = f"System: {memory_info['system_memory_percent']:.1f}% ({memory_info['system_memory_available_gb']:.1f}GB free)"
system_color = '#d9534f' if memory_info['system_memory_percent'] > 85 else '#5cb85c' system_color = colors['error'] if memory_info['system_memory_percent'] > 85 else colors['success']
else: else:
system_text = "System: N/A" system_text = "System: N/A"
system_color = '#777' system_color = colors['text_light']
self.system_memory_label.setText(system_text) self.system_memory_label.setText(system_text)
self.system_memory_label.setStyleSheet(f'font-size: 9pt; color: {system_color};') self.system_memory_label.setStyleSheet(f'font-size: 9pt; color: {system_color};')
# Cache info # Cache info
cache_text = f"Cache: {memory_info['template_cache_size']} templates, {memory_info['cooldown_entries']} cooldowns" cache_text = f"Cache: {memory_info['template_cache_size']} templates, {memory_info['cooldown_entries']} cooldowns"
cache_color = '#f0ad4e' if memory_info['template_cache_size'] > 20 else '#5bc0de' cache_color = colors['warning'] if memory_info['template_cache_size'] > 20 else colors['info']
self.cache_label.setText(cache_text) self.cache_label.setText(cache_text)
self.cache_label.setStyleSheet(f'font-size: 9pt; color: {cache_color};') self.cache_label.setStyleSheet(f'font-size: 9pt; color: {cache_color};')
@@ -1464,23 +1503,23 @@ class MainWindow(QtWidgets.QMainWindow):
if gpu_total_mb > 0: if gpu_total_mb > 0:
gpu_text = f"GPU: {gpu_used_mb:.0f}/{gpu_total_mb:.0f}MB ({gpu_utilization:.1f}%)" gpu_text = f"GPU: {gpu_used_mb:.0f}/{gpu_total_mb:.0f}MB ({gpu_utilization:.1f}%)"
gpu_color = '#d9534f' if gpu_utilization > 80 else '#f0ad4e' if gpu_utilization > 60 else '#5cb85c' gpu_color = colors['error'] if gpu_utilization > 80 else colors['warning'] if gpu_utilization > 60 else colors['success']
# Add GPU name as tooltip # Add GPU name as tooltip
gpu_name = memory_info.get('gpu_name', 'Unknown GPU') gpu_name = memory_info.get('gpu_name', 'Unknown GPU')
self.gpu_label.setToolTip(f"GPU: {gpu_name} (detected via {gpu_method})") self.gpu_label.setToolTip(f"GPU: {gpu_name} (detected via {gpu_method})")
else: else:
gpu_text = f"GPU: Detected ({gpu_method})" gpu_text = f"GPU: Detected ({gpu_method})"
gpu_color = '#5bc0de' gpu_color = colors['info']
gpu_name = memory_info.get('gpu_name', 'Unknown GPU') gpu_name = memory_info.get('gpu_name', 'Unknown GPU')
self.gpu_label.setToolTip(f"GPU: {gpu_name} (limited info via {gpu_method})") self.gpu_label.setToolTip(f"GPU: {gpu_name} (limited info via {gpu_method})")
elif gpu_method == 'loading': elif gpu_method == 'loading':
gpu_text = "GPU: Loading..." gpu_text = "GPU: Loading..."
gpu_color = '#f0ad4e' gpu_color = colors['warning']
self.gpu_label.setToolTip("GPU detection in progress...") self.gpu_label.setToolTip("GPU detection in progress...")
else: else:
gpu_text = "GPU: Not detected" gpu_text = "GPU: Not detected"
gpu_color = '#777' gpu_color = colors['text_light']
self.gpu_label.setToolTip( self.gpu_label.setToolTip(
"No GPU detected or GPU monitoring libraries not available.\n\n" "No GPU detected or GPU monitoring libraries not available.\n\n"
"For enhanced GPU monitoring:\n" "For enhanced GPU monitoring:\n"
@@ -1507,10 +1546,10 @@ class MainWindow(QtWidgets.QMainWindow):
if hasattr(self, '_loop_times') and self._loop_times: if hasattr(self, '_loop_times') and self._loop_times:
avg_time = sum(self._loop_times) / len(self._loop_times) avg_time = sum(self._loop_times) / len(self._loop_times)
perf_text = f"Performance: {avg_time*1000:.0f}ms avg loop time" perf_text = f"Performance: {avg_time*1000:.0f}ms avg loop time"
perf_color = '#d9534f' if avg_time > 0.5 else '#5cb85c' perf_color = colors['error'] if avg_time > 0.5 else colors['success']
else: else:
perf_text = "Performance: Not running" perf_text = "Performance: Not running"
perf_color = '#777' perf_color = colors['text_light']
self.performance_label.setText(perf_text) self.performance_label.setText(perf_text)
self.performance_label.setStyleSheet(f'font-size: 9pt; color: {perf_color};') self.performance_label.setStyleSheet(f'font-size: 9pt; color: {perf_color};')
@@ -1518,22 +1557,32 @@ class MainWindow(QtWidgets.QMainWindow):
# Show error if any # Show error if any
if 'error' in memory_info: if 'error' in memory_info:
self.performance_label.setText(f"Error: {memory_info['error'][:50]}...") self.performance_label.setText(f"Error: {memory_info['error'][:50]}...")
self.performance_label.setStyleSheet('font-size: 9pt; color: #d9534f;') self.performance_label.setStyleSheet(f'font-size: 9pt; color: {colors["error"]};')
except Exception as e: except Exception as e:
logger.debug(f"Error updating resource display: {e}") logger.debug(f"Error updating resource display: {e}")
# Show basic fallback info colors = self.get_theme_styles()
# Show basic fallback info with theme-aware colors
error_color = colors['error']
self.memory_label.setText("Memory: Error") self.memory_label.setText("Memory: Error")
self.memory_label.setStyleSheet(f'font-size: 9pt; color: {error_color};')
self.cpu_label.setText("CPU: Error") self.cpu_label.setText("CPU: Error")
self.cpu_label.setStyleSheet(f'font-size: 9pt; color: {error_color};')
self.system_memory_label.setText("System: Error") self.system_memory_label.setText("System: Error")
self.system_memory_label.setStyleSheet(f'font-size: 9pt; color: {error_color};')
self.cache_label.setText("Cache: Error") self.cache_label.setText("Cache: Error")
self.cache_label.setStyleSheet(f'font-size: 9pt; color: {error_color};')
self.gpu_label.setText("GPU: Error") self.gpu_label.setText("GPU: Error")
self.gpu_label.setStyleSheet(f'font-size: 9pt; color: {error_color};')
self.performance_label.setText(f"Display Error: {str(e)[:30]}...") self.performance_label.setText(f"Display Error: {str(e)[:30]}...")
self.performance_label.setStyleSheet(f'font-size: 9pt; color: {error_color};')
def stop_monitoring(self): def stop_monitoring(self):
"""Stop performance monitoring timer.""" """Stop performance monitoring and theme monitoring timers."""
if hasattr(self, 'monitor_timer') and self.monitor_timer: if hasattr(self, 'monitor_timer') and self.monitor_timer:
self.monitor_timer.stop() self.monitor_timer.stop()
if hasattr(self, 'theme_monitor_timer') and self.theme_monitor_timer:
self.theme_monitor_timer.stop()
def _toggle_resource_display(self): def _toggle_resource_display(self):
"""Toggle the visibility of resource usage widgets.""" """Toggle the visibility of resource usage widgets."""
@@ -1782,26 +1831,296 @@ class MainWindow(QtWidgets.QMainWindow):
logger.info("Resources cleaned up successfully") logger.info("Resources cleaned up successfully")
def is_system_dark_theme(self):
"""
Detect if the system is using dark theme.
Works on Windows 10/11, macOS, and some Linux desktop environments.
"""
try:
import platform
system = platform.system().lower()
if system == 'windows':
# Windows 10/11 dark mode detection
try:
import winreg
registry = winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER)
key = winreg.OpenKey(registry, r"SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize")
value, _ = winreg.QueryValueEx(key, "AppsUseLightTheme")
winreg.CloseKey(key)
return value == 0 # 0 = dark theme, 1 = light theme
except (ImportError, FileNotFoundError, OSError):
# Fallback: check if PyQt6 can detect system dark theme
try:
palette = QtWidgets.QApplication.palette()
# Check if the window background is darker than text color
bg_color = palette.color(QtGui.QPalette.ColorRole.Window)
text_color = palette.color(QtGui.QPalette.ColorRole.WindowText)
# Calculate luminance to determine if background is dark
bg_luminance = (0.299 * bg_color.red() + 0.587 * bg_color.green() + 0.114 * bg_color.blue()) / 255
return bg_luminance < 0.5
except:
pass
elif system == 'darwin': # macOS
try:
import subprocess
result = subprocess.run(['defaults', 'read', '-g', 'AppleInterfaceStyle'],
capture_output=True, text=True, timeout=2)
return result.stdout.strip().lower() == 'dark'
except (subprocess.CalledProcessError, subprocess.TimeoutExpired, FileNotFoundError):
pass
elif system == 'linux':
# Try to detect dark theme on Linux (GNOME, KDE, etc.)
try:
import subprocess
import os
# Try GNOME gsettings
try:
result = subprocess.run(['gsettings', 'get', 'org.gnome.desktop.interface', 'gtk-theme'],
capture_output=True, text=True, timeout=2)
theme_name = result.stdout.strip().strip("'").lower()
return any(dark_indicator in theme_name for dark_indicator in ['dark', 'adwaita-dark', 'breeze-dark'])
except (subprocess.CalledProcessError, subprocess.TimeoutExpired, FileNotFoundError):
pass
# Try KDE configuration
kde_config_paths = [
os.path.expanduser('~/.config/kdeglobals'),
os.path.expanduser('~/.kde/share/config/kdeglobals'),
os.path.expanduser('~/.kde4/share/config/kdeglobals')
]
for config_path in kde_config_paths:
try:
if os.path.exists(config_path):
with open(config_path, 'r') as f:
content = f.read().lower()
if 'colorscheme=breezedark' in content or 'dark' in content:
return True
except:
continue
# Fallback: check environment variables
desktop_env = os.environ.get('XDG_CURRENT_DESKTOP', '').lower()
if 'dark' in desktop_env:
return True
except:
pass
except Exception as e:
logger.debug(f"System theme detection failed: {e}")
# Default fallback: assume light theme
return False
def get_effective_dark_mode(self):
"""
Get the effective dark mode state based on current theme setting.
"""
if self.theme_mode == 'dark':
return True
elif self.theme_mode == 'light':
return False
else: # system
return self.is_system_dark_theme()
def get_theme_styles(self):
"""
Get the current theme styles (light or dark mode).
"""
is_dark = self.get_effective_dark_mode()
if is_dark:
return {
'background': '#2b2b2b',
'foreground': '#ffffff',
'button_bg': '#404040',
'button_hover': '#505050',
'input_bg': '#3c3c3c',
'border': '#555555',
'accent': '#0078d4',
'success': '#107c10',
'warning': '#ff8c00',
'error': '#d13438',
'info': '#0078d4',
'text_light': '#cccccc',
'text_dark': '#ffffff'
}
else:
return {
'background': '#ffffff',
'foreground': '#000000',
'button_bg': '#f0f0f0',
'button_hover': '#e0e0e0',
'input_bg': '#ffffff',
'border': '#cccccc',
'accent': '#0055aa',
'success': '#107c10',
'warning': '#ff8c00',
'error': '#d9534f',
'info': '#0055aa',
'text_light': '#333333',
'text_dark': '#000000'
}
def apply_theme(self):
"""
Apply the current theme to all UI elements.
"""
colors = self.get_theme_styles()
# Main window style
main_style = f"""
QMainWindow {{
background-color: {colors['background']};
color: {colors['foreground']};
}}
QPushButton {{
font-size: 9pt;
min-height: 20px;
padding: 2px 6px;
min-width: 70px;
background-color: {colors['button_bg']};
border: 1px solid {colors['border']};
border-radius: 3px;
color: {colors['foreground']};
}}
QPushButton:hover {{
background-color: {colors['button_hover']};
}}
QPushButton:pressed {{
background-color: {colors['border']};
}}
QComboBox {{
font-size: 9pt;
min-height: 20px;
padding: 2px 6px;
min-width: 90px;
background-color: {colors['input_bg']};
border: 1px solid {colors['border']};
border-radius: 3px;
color: {colors['foreground']};
}}
QComboBox::drop-down {{
border: none;
width: 20px;
}}
QComboBox::down-arrow {{
image: none;
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-top: 4px solid {colors['foreground']};
margin: 0px 4px 0px 4px;
}}
QListWidget {{
font-size: 9pt;
min-width: 200px;
min-height: 120px;
background-color: {colors['input_bg']};
border: 1px solid {colors['border']};
border-radius: 3px;
color: {colors['foreground']};
}}
QListWidget::item {{
padding: 4px;
border-bottom: 1px solid {colors['border']};
}}
QListWidget::item:selected {{
background-color: {colors['accent']};
color: white;
}}
QLabel {{
font-size: 9pt;
color: {colors['foreground']};
}}
QGroupBox {{
font-size: 9pt;
font-weight: bold;
border: 1px solid {colors['border']};
border-radius: 5px;
margin-top: 10px;
padding-top: 5px;
color: {colors['foreground']};
}}
QGroupBox::title {{
subcontrol-origin: margin;
left: 10px;
padding: 0 5px 0 5px;
color: {colors['foreground']};
}}
QWidget {{
color: {colors['foreground']};
background-color: {colors['background']};
}}
"""
self.setStyleSheet(main_style)
# Update individual labels with theme-appropriate colors
self.state_label.setStyleSheet(f'font-weight: bold; font-size: 11pt; color: {colors["accent"]};')
# Update resource labels
self.memory_label.setStyleSheet(f'font-size: 9pt; color: {colors["text_light"]};')
self.cpu_label.setStyleSheet(f'font-size: 9pt; color: {colors["text_light"]};')
self.system_memory_label.setStyleSheet(f'font-size: 9pt; color: {colors["text_light"]};')
self.cache_label.setStyleSheet(f'font-size: 9pt; color: {colors["text_light"]};')
self.gpu_label.setStyleSheet(f'font-size: 9pt; color: {colors["text_light"]};')
self.performance_label.setStyleSheet(f'font-size: 9pt; color: {colors["text_light"]};')
self.interval_label.setStyleSheet(f'font-size: 9pt; color: {colors["text_light"]};')
def _update_theme_button_text(self):
"""
Update the theme button text based on current theme mode.
"""
if self.theme_mode == 'system':
system_dark = self.is_system_dark_theme()
theme_text = f"System ({'Dark' if system_dark else 'Light'})"
elif self.theme_mode == 'light':
theme_text = "Light"
else: # dark
theme_text = "Dark"
if hasattr(self, 'btn_theme'):
self.btn_theme.setText(theme_text)
def toggle_theme(self):
"""
Cycle through theme modes: System -> Light -> Dark -> System...
"""
if self.theme_mode == 'system':
self.theme_mode = 'light'
elif self.theme_mode == 'light':
self.theme_mode = 'dark'
else: # dark
self.theme_mode = 'system'
# Start/stop theme monitoring based on mode
if self.theme_mode == 'system':
self._last_system_theme = self.is_system_dark_theme()
if hasattr(self, 'theme_monitor_timer'):
self.theme_monitor_timer.start(5000) # Check every 5 seconds
else:
if hasattr(self, 'theme_monitor_timer'):
self.theme_monitor_timer.stop()
self.apply_theme()
self._update_theme_button_text()
logger.info(f'Theme changed to: {self.theme_mode} mode')
def init_ui(self): def init_ui(self):
""" """
Initializes the main UI components and layouts. Initializes the main UI components and layouts.
Sets up scenario and window dropdowns, step list, and action buttons. Sets up scenario and window dropdowns, step list, and action buttons.
""" """
# Set compact font and style # Set compact font
font = QtGui.QFont() font = QtGui.QFont()
font.setPointSize(9) font.setPointSize(9)
self.setFont(font) self.setFont(font)
style = """
QPushButton, QComboBox, QListWidget, QLabel {
font-size: 9pt;
min-height: 20px;
padding: 2px 6px;
}
QComboBox { min-width: 90px; }
QPushButton { min-width: 70px; }
QListWidget { min-width: 200px; min-height: 120px; }
"""
self.setStyleSheet(style)
# Scenario group (QGroupBox with title 'Scenario') # Scenario group (QGroupBox with title 'Scenario')
scenario_group_box = QtWidgets.QGroupBox('Scenario') scenario_group_box = QtWidgets.QGroupBox('Scenario')
@@ -1898,9 +2217,14 @@ class MainWindow(QtWidgets.QMainWindow):
self.btn_start_stop.setMinimumWidth(80) self.btn_start_stop.setMinimumWidth(80)
self.btn_start_stop.clicked.connect(self._log_btn_start_stop) self.btn_start_stop.clicked.connect(self._log_btn_start_stop)
# Theme toggle button
self.btn_theme = QtWidgets.QPushButton('System')
self.btn_theme.setMinimumWidth(80)
self.btn_theme.setToolTip('Cycle through theme modes: System (follows OS) -> Light -> Dark -> System...')
self.btn_theme.clicked.connect(self.toggle_theme)
# State label # State label
self.state_label = QtWidgets.QLabel('State: Paused') self.state_label = QtWidgets.QLabel('State: Paused')
self.state_label.setStyleSheet('font-weight: bold; font-size: 11pt; color: #0055aa;')
self.state_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft | QtCore.Qt.AlignmentFlag.AlignVCenter) self.state_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft | QtCore.Qt.AlignmentFlag.AlignVCenter)
# Resource usage display # Resource usage display
@@ -1912,32 +2236,26 @@ class MainWindow(QtWidgets.QMainWindow):
# Memory usage # Memory usage
self.memory_label = QtWidgets.QLabel('Memory: --') self.memory_label = QtWidgets.QLabel('Memory: --')
self.memory_label.setStyleSheet('font-size: 9pt; color: #333;')
resource_layout.addWidget(self.memory_label, 0, 0) resource_layout.addWidget(self.memory_label, 0, 0)
# CPU usage # CPU usage
self.cpu_label = QtWidgets.QLabel('CPU: --') self.cpu_label = QtWidgets.QLabel('CPU: --')
self.cpu_label.setStyleSheet('font-size: 9pt; color: #333;')
resource_layout.addWidget(self.cpu_label, 0, 1) resource_layout.addWidget(self.cpu_label, 0, 1)
# System memory # System memory
self.system_memory_label = QtWidgets.QLabel('System: --') self.system_memory_label = QtWidgets.QLabel('System: --')
self.system_memory_label.setStyleSheet('font-size: 9pt; color: #333;')
resource_layout.addWidget(self.system_memory_label, 1, 0) resource_layout.addWidget(self.system_memory_label, 1, 0)
# Cache info # Cache info
self.cache_label = QtWidgets.QLabel('Cache: --') self.cache_label = QtWidgets.QLabel('Cache: --')
self.cache_label.setStyleSheet('font-size: 9pt; color: #333;')
resource_layout.addWidget(self.cache_label, 1, 1) resource_layout.addWidget(self.cache_label, 1, 1)
# GPU memory info # GPU memory info
self.gpu_label = QtWidgets.QLabel('GPU: --') self.gpu_label = QtWidgets.QLabel('GPU: --')
self.gpu_label.setStyleSheet('font-size: 9pt; color: #333;')
resource_layout.addWidget(self.gpu_label, 1, 2) resource_layout.addWidget(self.gpu_label, 1, 2)
# Performance info # Performance info
self.performance_label = QtWidgets.QLabel('Performance: --') self.performance_label = QtWidgets.QLabel('Performance: --')
self.performance_label.setStyleSheet('font-size: 9pt; color: #333;')
resource_layout.addWidget(self.performance_label, 2, 0, 1, 3) resource_layout.addWidget(self.performance_label, 2, 0, 1, 3)
# Controls row (toggle button and update interval) - placed in a separate row # Controls row (toggle button and update interval) - placed in a separate row
@@ -1949,7 +2267,6 @@ class MainWindow(QtWidgets.QMainWindow):
# Update interval controls # Update interval controls
self.interval_label = QtWidgets.QLabel('Update:') self.interval_label = QtWidgets.QLabel('Update:')
self.interval_label.setStyleSheet('font-size: 9pt; color: #333;')
self.update_interval_combo = QtWidgets.QComboBox() self.update_interval_combo = QtWidgets.QComboBox()
self.update_interval_combo.setMaximumWidth(80) self.update_interval_combo.setMaximumWidth(80)
self.update_interval_combo.setToolTip('Set resource monitoring update interval') self.update_interval_combo.setToolTip('Set resource monitoring update interval')
@@ -1998,8 +2315,9 @@ class MainWindow(QtWidgets.QMainWindow):
start_state_group.setTitle("") start_state_group.setTitle("")
start_state_group.setSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Fixed) start_state_group.setSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Fixed)
start_state_layout = QtWidgets.QHBoxLayout() start_state_layout = QtWidgets.QHBoxLayout()
start_state_layout.setSpacing(6) # Fixed gap between button and label start_state_layout.setSpacing(6) # Fixed gap between elements
start_state_layout.addWidget(self.btn_start_stop) start_state_layout.addWidget(self.btn_start_stop)
start_state_layout.addWidget(self.btn_theme)
start_state_layout.addWidget(self.state_label) start_state_layout.addWidget(self.state_label)
start_state_layout.addStretch(1) start_state_layout.addStretch(1)
start_state_group.setLayout(start_state_layout) start_state_group.setLayout(start_state_layout)
@@ -2010,6 +2328,10 @@ class MainWindow(QtWidgets.QMainWindow):
central = QtWidgets.QWidget() central = QtWidgets.QWidget()
central.setLayout(layout) central.setLayout(layout)
self.setCentralWidget(central) self.setCentralWidget(central)
# Apply initial theme and set button text
self.apply_theme()
self._update_theme_button_text()
def _log_btn_refresh_windows(self): def _log_btn_refresh_windows(self):
logger.info("UI: Refresh Windows button pressed") logger.info("UI: Refresh Windows button pressed")