Skip to content

Commit 423be21

Browse files
committed
feat(cleanup): register atexit handlers to clear events on exit
Added atexit handlers in ActiveWindowWidget, CavaWidget, and TaskbarWidget to ensure proper cleanup of event services when the application exits. This improves resource management and prevents potential memory leaks.
1 parent 3457f18 commit 423be21

File tree

3 files changed

+24
-10
lines changed

3 files changed

+24
-10
lines changed

src/core/widgets/yasb/active_window.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import win32gui
1313
from core.utils.win32.app_icons import get_window_icon
1414
from core.utils.widgets.animation_manager import AnimationManager
15+
import atexit
1516

1617
IGNORED_TITLES = ['', ' ', 'FolderView', 'Program Manager', 'python3', 'pythonw3', 'YasbBar', 'Search', 'Start', 'yasb']
1718
IGNORED_CLASSES = ['WorkerW', 'TopLevelWindowForOverflowXamlIsland', 'Shell_TrayWnd', 'Shell_SecondaryTrayWnd']
@@ -121,8 +122,12 @@ def __init__(
121122

122123
self.focus_change_workspaces.connect(self._on_focus_change_workspaces)
123124
self._event_service.register_event("workspace_update", self.focus_change_workspaces)
124-
125+
126+
atexit.register(self._stop_events)
125127

128+
def _stop_events(self) -> None:
129+
self._event_service.clear()
130+
126131
def _on_focus_change_workspaces(self, event: str) -> None:
127132
# Temporary fix for MoveWindow event from Komorebi: MoveWindow event is not sending enough data to know on which monitor the window is being moved also animation is a problem and because of that we are using singleShot to try catch the window after the animation is done and this will run only on MoveWindow event
128133
if event in ['Hide', 'Destroy']:

src/core/widgets/yasb/cava.py

+13-6
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from PyQt6.QtWidgets import QHBoxLayout, QWidget, QLabel, QApplication
1212
from PyQt6.QtGui import QLinearGradient, QPainter, QColor
1313
from PyQt6.QtCore import QTimer, pyqtSignal
14+
import atexit
1415

1516
class CavaBar(QWidget):
1617
def __init__(self, cava_widget):
@@ -122,8 +123,8 @@ def __init__(
122123

123124
# Connect signal and start audio processing
124125
self.samplesUpdated.connect(self.on_samples_updated)
126+
self.destroyed.connect(self.stop_cava)
125127
self.start_cava()
126-
127128

128129
# Set up auto-hide timer for silence
129130
if self._hide_empty and self._sleep_timer > 0:
@@ -134,12 +135,16 @@ def __init__(
134135

135136
if QApplication.instance():
136137
QApplication.instance().aboutToQuit.connect(self.stop_cava)
138+
atexit.register(self.stop_cava)
137139

138140
def stop_cava(self) -> None:
139141
self._stop_cava = True
142+
if hasattr(self, "_cava_process") and self._cava_process.poll() is None:
143+
self._cava_process.terminate()
140144
if hasattr(self, "thread_cava") and self.thread_cava.is_alive():
141-
self.thread_cava.join()
142-
145+
if threading.current_thread() != self.thread_cava:
146+
self.thread_cava.join()
147+
143148
def initialize_colors(self) -> None:
144149
self.foreground_color = QColor(self._foreground)
145150
if self._gradient == 1:
@@ -169,6 +174,7 @@ def hide_bar_frame(self) -> None:
169174
self.hide()
170175
self._hide_cava_widget = True
171176

177+
172178
def start_cava(self) -> None:
173179
# Build configuration file, temp config file will be created in %temp% directory
174180
config_template = textwrap.dedent(f"""\
@@ -211,7 +217,7 @@ def process_audio():
211217
with open(cava_config_path, "w") as config_file:
212218
config_file.write(config_template)
213219
config_file.flush()
214-
process = subprocess.Popen(
220+
self._cava_process = subprocess.Popen(
215221
["cava", "-p", cava_config_path],
216222
stdout=subprocess.PIPE,
217223
creationflags=subprocess.CREATE_NO_WINDOW
@@ -220,7 +226,7 @@ def process_audio():
220226
fmt = bytetype * self._bars_number
221227
while True:
222228
try:
223-
data = process.stdout.read(chunk)
229+
data = self._cava_process.stdout.read(chunk)
224230
except Exception as e:
225231
return
226232
if len(data) < chunk:
@@ -231,8 +237,9 @@ def process_audio():
231237
self.samplesUpdated.emit(samples)
232238
except Exception as e:
233239
logging.error(f"Error processing audio in Cava: {e}")
240+
self.stop_cava()
234241
finally:
235-
process.terminate()
242+
self.stop_cava()
236243

237244
self.thread_cava = threading.Thread(target=process_audio, daemon=True)
238245
self.thread_cava.start()

src/core/widgets/yasb/taskbar.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from PIL import Image
1414
import win32gui
1515
import win32con
16+
import atexit
1617

1718
try:
1819
from core.utils.win32.event_listener import SystemEventListener
@@ -94,9 +95,10 @@ def __init__(
9495
self._debounced_foreground_event = None
9596

9697
if QApplication.instance():
97-
QApplication.instance().aboutToQuit.connect(self.stop_taskbar_events)
98-
99-
def stop_taskbar_events(self) -> None:
98+
QApplication.instance().aboutToQuit.connect(self._stop_events)
99+
atexit.register(self._stop_events)
100+
101+
def _stop_events(self) -> None:
100102
self._event_service.clear()
101103

102104
def _on_update_event(self, hwnd: int, event: WinEvent) -> None:

0 commit comments

Comments
 (0)