fix: add logger import and safe widget access for HUD rebuild

- Add logging import and logger instance
- Add _safe_set_text() helper to handle deleted widgets gracefully
- Use safe access in _refresh_display to prevent RuntimeError on rebuild
- Wrap stylesheet updates in try/except for safety
This commit is contained in:
LemonNexus 2026-02-09 22:55:22 +00:00
parent 7a55e3b246
commit 64a68f3857
1 changed files with 50 additions and 47 deletions

View File

@ -5,12 +5,15 @@ Cleaner, customizable HUD with collapsible sections.
import sys import sys
import json import json
import logging
from pathlib import Path from pathlib import Path
from decimal import Decimal from decimal import Decimal
from datetime import datetime, timedelta from datetime import datetime, timedelta
from dataclasses import dataclass, asdict, field from dataclasses import dataclass, asdict, field
from typing import Optional, Dict, Any, List, Set from typing import Optional, Dict, Any, List, Set
logger = logging.getLogger(__name__)
# Windows-specific imports for click-through support # Windows-specific imports for click-through support
if sys.platform == 'win32': if sys.platform == 'win32':
import ctypes import ctypes
@ -659,72 +662,72 @@ class HUDOverlay(QWidget):
if hasattr(self, 'time_label'): if hasattr(self, 'time_label'):
self.time_label.setText(f"{hours:02d}:{minutes:02d}:{seconds:02d}") self.time_label.setText(f"{hours:02d}:{minutes:02d}:{seconds:02d}")
def _safe_set_text(self, widget_name: str, text: str):
"""Safely set text on a widget if it exists and is valid."""
try:
widget = getattr(self, widget_name, None)
if widget is not None:
widget.setText(text)
except RuntimeError:
# Widget was deleted
pass
def _refresh_display(self): def _refresh_display(self):
"""Refresh all display labels.""" """Refresh all display labels."""
# Profit/Loss # Profit/Loss
if hasattr(self, 'profit_label'): profit = self._stats.profit_loss
profit = self._stats.profit_loss color = "#7FFF7F" if profit >= 0 else "#FF7F7F"
color = "#7FFF7F" if profit >= 0 else "#FF7F7F" self._safe_set_text('profit_label', f"{profit:+.2f} PED")
self.profit_label.setText(f"{profit:+.2f} PED") if hasattr(self, 'profit_label') and self.profit_label is not None:
self.profit_label.setStyleSheet(f"font-size: 18px; font-weight: bold; color: {color};") try:
self.profit_label.setStyleSheet(f"font-size: 18px; font-weight: bold; color: {color};")
except RuntimeError:
pass
# Return % # Return %
if hasattr(self, 'return_label'): ret = self._stats.return_percentage
ret = self._stats.return_percentage if ret >= 100:
if ret >= 100: color = "#7FFF7F"
color = "#7FFF7F" elif ret >= 90:
elif ret >= 90: color = "#FFFF7F"
color = "#FFFF7F" else:
else: color = "#FF7F7F"
color = "#FF7F7F" self._safe_set_text('return_label', f"{ret:.1f}%")
self.return_label.setText(f"{ret:.1f}%") if hasattr(self, 'return_label') and self.return_label is not None:
self.return_label.setStyleSheet(f"font-size: 16px; font-weight: bold; color: {color};") try:
self.return_label.setStyleSheet(f"font-size: 16px; font-weight: bold; color: {color};")
except RuntimeError:
pass
# Total Cost + Loot # Total Cost + Loot
if hasattr(self, 'total_cost_label'): self._safe_set_text('total_cost_label', f"Cost: {self._stats.cost_total:.2f}")
self.total_cost_label.setText(f"Cost: {self._stats.cost_total:.2f}") self._safe_set_text('total_loot_label', f"Loot: {self._stats.loot_total:.2f}")
if hasattr(self, 'total_loot_label'):
self.total_loot_label.setText(f"Loot: {self._stats.loot_total:.2f}")
# Cost metrics # Cost metrics
if hasattr(self, 'cps_label'): self._safe_set_text('cps_label', f"Shot: {self._stats.cost_per_shot:.4f}")
self.cps_label.setText(f"Shot: {self._stats.cost_per_shot:.4f}") self._safe_set_text('cph_label', f"Hit: {self._stats.cost_per_hit:.4f}")
if hasattr(self, 'cph_label'): self._safe_set_text('cphl_label', f"Heal: {self._stats.cost_per_heal:.4f}")
self.cph_label.setText(f"Hit: {self._stats.cost_per_hit:.4f}")
if hasattr(self, 'cphl_label'):
self.cphl_label.setText(f"Heal: {self._stats.cost_per_heal:.4f}")
# Gear # Gear
if hasattr(self, 'weapon_label'): self._safe_set_text('weapon_label', f"🔫 {self._stats.current_weapon[:20]}")
self.weapon_label.setText(f"🔫 {self._stats.current_weapon[:20]}") self._safe_set_text('armor_label', f"🛡️ {self._stats.current_armor[:20]}")
if hasattr(self, 'armor_label'): self._safe_set_text('loadout_label', f"📋 {self._stats.current_loadout[:20]}")
self.armor_label.setText(f"🛡️ {self._stats.current_armor[:20]}")
if hasattr(self, 'loadout_label'):
self.loadout_label.setText(f"📋 {self._stats.current_loadout[:20]}")
# Cost breakdown # Cost breakdown
if hasattr(self, 'wep_cost_label'): self._safe_set_text('wep_cost_label', f"{self._stats.weapon_cost_total:.2f}")
self.wep_cost_label.setText(f"{self._stats.weapon_cost_total:.2f}") self._safe_set_text('arm_cost_label', f"{self._stats.armor_cost_total:.2f}")
if hasattr(self, 'arm_cost_label'): self._safe_set_text('heal_cost_label', f"{self._stats.healing_cost_total:.2f}")
self.arm_cost_label.setText(f"{self._stats.armor_cost_total:.2f}")
if hasattr(self, 'heal_cost_label'):
self.heal_cost_label.setText(f"{self._stats.healing_cost_total:.2f}")
# Combat # Combat
if hasattr(self, 'kills_label'): self._safe_set_text('kills_label', f"Kills: {self._stats.kills}")
self.kills_label.setText(f"Kills: {self._stats.kills}") self._safe_set_text('globals_label', f"Globals: {self._stats.globals_count}")
if hasattr(self, 'globals_label'):
self.globals_label.setText(f"Globals: {self._stats.globals_count}")
# Damage stats # Damage stats
if hasattr(self, 'damage_dealt_label'): self._safe_set_text('damage_dealt_label', f"Dealt: {int(self._stats.damage_dealt)}")
self.damage_dealt_label.setText(f"Dealt: {int(self._stats.damage_dealt)}") self._safe_set_text('damage_taken_label', f"Taken: {int(self._stats.damage_taken)}")
if hasattr(self, 'damage_taken_label'):
self.damage_taken_label.setText(f"Taken: {int(self._stats.damage_taken)}")
# Shrapnel # Shrapnel
if hasattr(self, 'shrapnel_label'): self._safe_set_text('shrapnel_label', f"💎 Shrapnel: {self._stats.shrapnel_total:.2f} PED")
self.shrapnel_label.setText(f"💎 Shrapnel: {self._stats.shrapnel_total:.2f} PED")
# === Public Update Methods === # === Public Update Methods ===