From 64a68f3857dd5eba498107e6f1743c5e3cc9e349 Mon Sep 17 00:00:00 2001 From: LemonNexus Date: Mon, 9 Feb 2026 22:55:22 +0000 Subject: [PATCH] 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 --- ui/hud_overlay_clean.py | 97 +++++++++++++++++++++-------------------- 1 file changed, 50 insertions(+), 47 deletions(-) diff --git a/ui/hud_overlay_clean.py b/ui/hud_overlay_clean.py index 584e866..2f343bd 100644 --- a/ui/hud_overlay_clean.py +++ b/ui/hud_overlay_clean.py @@ -5,12 +5,15 @@ Cleaner, customizable HUD with collapsible sections. import sys import json +import logging from pathlib import Path from decimal import Decimal from datetime import datetime, timedelta from dataclasses import dataclass, asdict, field from typing import Optional, Dict, Any, List, Set +logger = logging.getLogger(__name__) + # Windows-specific imports for click-through support if sys.platform == 'win32': import ctypes @@ -659,72 +662,72 @@ class HUDOverlay(QWidget): if hasattr(self, 'time_label'): 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): """Refresh all display labels.""" # Profit/Loss - if hasattr(self, 'profit_label'): - profit = self._stats.profit_loss - color = "#7FFF7F" if profit >= 0 else "#FF7F7F" - self.profit_label.setText(f"{profit:+.2f} PED") - self.profit_label.setStyleSheet(f"font-size: 18px; font-weight: bold; color: {color};") + profit = self._stats.profit_loss + color = "#7FFF7F" if profit >= 0 else "#FF7F7F" + self._safe_set_text('profit_label', f"{profit:+.2f} PED") + if hasattr(self, 'profit_label') and self.profit_label is not None: + try: + self.profit_label.setStyleSheet(f"font-size: 18px; font-weight: bold; color: {color};") + except RuntimeError: + pass # Return % - if hasattr(self, 'return_label'): - ret = self._stats.return_percentage - if ret >= 100: - color = "#7FFF7F" - elif ret >= 90: - color = "#FFFF7F" - else: - color = "#FF7F7F" - self.return_label.setText(f"{ret:.1f}%") - self.return_label.setStyleSheet(f"font-size: 16px; font-weight: bold; color: {color};") + ret = self._stats.return_percentage + if ret >= 100: + color = "#7FFF7F" + elif ret >= 90: + color = "#FFFF7F" + else: + color = "#FF7F7F" + self._safe_set_text('return_label', f"{ret:.1f}%") + if hasattr(self, 'return_label') and self.return_label is not None: + try: + self.return_label.setStyleSheet(f"font-size: 16px; font-weight: bold; color: {color};") + except RuntimeError: + pass # Total Cost + Loot - if hasattr(self, 'total_cost_label'): - self.total_cost_label.setText(f"Cost: {self._stats.cost_total:.2f}") - if hasattr(self, 'total_loot_label'): - self.total_loot_label.setText(f"Loot: {self._stats.loot_total:.2f}") + self._safe_set_text('total_cost_label', f"Cost: {self._stats.cost_total:.2f}") + self._safe_set_text('total_loot_label', f"Loot: {self._stats.loot_total:.2f}") # Cost metrics - if hasattr(self, 'cps_label'): - self.cps_label.setText(f"Shot: {self._stats.cost_per_shot:.4f}") - if hasattr(self, 'cph_label'): - 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}") + self._safe_set_text('cps_label', f"Shot: {self._stats.cost_per_shot:.4f}") + self._safe_set_text('cph_label', f"Hit: {self._stats.cost_per_hit:.4f}") + self._safe_set_text('cphl_label', f"Heal: {self._stats.cost_per_heal:.4f}") # Gear - if hasattr(self, 'weapon_label'): - self.weapon_label.setText(f"🔫 {self._stats.current_weapon[:20]}") - if hasattr(self, 'armor_label'): - 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]}") + self._safe_set_text('weapon_label', f"🔫 {self._stats.current_weapon[:20]}") + self._safe_set_text('armor_label', f"🛡️ {self._stats.current_armor[:20]}") + self._safe_set_text('loadout_label', f"📋 {self._stats.current_loadout[:20]}") # Cost breakdown - if hasattr(self, 'wep_cost_label'): - self.wep_cost_label.setText(f"{self._stats.weapon_cost_total:.2f}") - if hasattr(self, 'arm_cost_label'): - 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}") + self._safe_set_text('wep_cost_label', f"{self._stats.weapon_cost_total:.2f}") + self._safe_set_text('arm_cost_label', f"{self._stats.armor_cost_total:.2f}") + self._safe_set_text('heal_cost_label', f"{self._stats.healing_cost_total:.2f}") # Combat - if hasattr(self, 'kills_label'): - self.kills_label.setText(f"Kills: {self._stats.kills}") - if hasattr(self, 'globals_label'): - self.globals_label.setText(f"Globals: {self._stats.globals_count}") + self._safe_set_text('kills_label', f"Kills: {self._stats.kills}") + self._safe_set_text('globals_label', f"Globals: {self._stats.globals_count}") # Damage stats - if hasattr(self, 'damage_dealt_label'): - self.damage_dealt_label.setText(f"Dealt: {int(self._stats.damage_dealt)}") - if hasattr(self, 'damage_taken_label'): - self.damage_taken_label.setText(f"Taken: {int(self._stats.damage_taken)}") + self._safe_set_text('damage_dealt_label', f"Dealt: {int(self._stats.damage_dealt)}") + self._safe_set_text('damage_taken_label', f"Taken: {int(self._stats.damage_taken)}") # Shrapnel - if hasattr(self, 'shrapnel_label'): - self.shrapnel_label.setText(f"💎 Shrapnel: {self._stats.shrapnel_total:.2f} PED") + self._safe_set_text('shrapnel_label', f"💎 Shrapnel: {self._stats.shrapnel_total:.2f} PED") # === Public Update Methods ===