1
1
import re
2
+ import ctypes
3
+ import logging
2
4
from core .widgets .base import BaseWidget
3
5
from core .validation .widgets .yasb .volume import VALIDATION_SCHEMA
4
6
from PyQt6 .QtWidgets import QLabel , QHBoxLayout , QWidget
5
- from PyQt6 .QtCore import Qt
7
+ from PyQt6 .QtCore import Qt , pyqtSignal
6
8
from PyQt6 .QtGui import QWheelEvent
7
- from comtypes import CLSCTX_ALL , CoInitialize , CoUninitialize
8
- from pycaw .pycaw import AudioUtilities , IAudioEndpointVolume
9
- import ctypes
10
- import logging
11
-
9
+ from comtypes import CLSCTX_ALL , CoInitialize , CoUninitialize , COMObject
10
+ from pycaw .pycaw import AudioUtilities , IAudioEndpointVolume , IAudioEndpointVolumeCallback
11
+ from pycaw .callbacks import MMNotificationClient
12
+
12
13
# Disable comtypes logging
13
14
logging .getLogger ('comtypes' ).setLevel (logging .CRITICAL )
14
15
15
16
# Constants from the Windows API
16
17
VK_VOLUME_UP = 0xAF
17
18
VK_VOLUME_DOWN = 0xAE
18
19
KEYEVENTF_KEYUP = 0x0002
19
- UPDATE_INTERVAL = 1000
20
-
20
+
21
+ class AudioEndpointChangeCallback (MMNotificationClient ):
22
+ def __init__ (self ,parent ):
23
+ super ().__init__ ()
24
+ self .parent = parent
25
+
26
+ def on_property_value_changed (self , device_id , property_struct , fmtid , pid ):
27
+ self .parent .update_label_signal .emit ()
28
+
29
+ class AudioEndpointVolumeCallback (COMObject ):
30
+ _com_interfaces_ = [IAudioEndpointVolumeCallback ]
31
+ def __init__ (self , parent ):
32
+ super ().__init__ ()
33
+ self .parent = parent
34
+ def OnNotify (self , pNotify ):
35
+ self .parent .update_label_signal .emit ()
36
+
37
+
21
38
class VolumeWidget (BaseWidget ):
22
39
validation_schema = VALIDATION_SCHEMA
40
+ update_label_signal = pyqtSignal ()
23
41
24
42
def __init__ (
25
43
self ,
@@ -28,7 +46,7 @@ def __init__(
28
46
volume_icons : list [str ],
29
47
callbacks : dict [str , str ]
30
48
):
31
- super ().__init__ (UPDATE_INTERVAL , class_name = "volume-widget" )
49
+ super ().__init__ (class_name = "volume-widget" )
32
50
self ._show_alt_label = False
33
51
self ._label_content = label
34
52
self ._label_alt_content = label_alt
@@ -49,10 +67,17 @@ def __init__(
49
67
self .callback_left = "toggle_mute"
50
68
self .callback_right = callbacks ["on_right" ]
51
69
self .callback_middle = callbacks ["on_middle" ]
52
- self .callback_timer = "update_label"
53
-
54
- self .start_timer ()
70
+
71
+ self .cb = AudioEndpointChangeCallback (self )
72
+ self .enumerator = AudioUtilities .GetDeviceEnumerator ()
73
+ self .enumerator .RegisterEndpointNotificationCallback (self .cb )
74
+
75
+ self ._initialize_volume_interface ()
76
+ self .update_label_signal .connect (self ._update_label )
77
+
78
+ self ._update_label ()
55
79
80
+
56
81
def _toggle_label (self ):
57
82
self ._show_alt_label = not self ._show_alt_label
58
83
for widget in self ._widgets :
@@ -96,7 +121,6 @@ def _update_label(self):
96
121
label_parts = re .split ('(<span.*?>.*?</span>)' , active_label_content )
97
122
label_parts = [part for part in label_parts if part ]
98
123
widget_index = 0
99
-
100
124
try :
101
125
self ._initialize_volume_interface ()
102
126
mute_status = self .volume .GetMute ()
@@ -130,13 +154,13 @@ def _get_volume_icon(self):
130
154
self .setToolTip (f'Volume { current_volume_level } ' )
131
155
if current_mute_status == 1 :
132
156
volume_icon = self ._volume_icons [0 ]
133
- elif ( current_volume_level >= 0 and current_volume_level < 11 ) :
157
+ elif 0 <= current_volume_level < 11 :
134
158
volume_icon = self ._volume_icons [1 ]
135
- elif ( current_volume_level >= 11 and current_volume_level < 30 ) :
159
+ elif 11 <= current_volume_level < 30 :
136
160
volume_icon = self ._volume_icons [2 ]
137
- elif ( current_volume_level >= 30 and current_volume_level < 60 ) :
161
+ elif 30 <= current_volume_level < 60 :
138
162
volume_icon = self ._volume_icons [3 ]
139
- elif ( current_volume_level >= 60 ) :
163
+ else :
140
164
volume_icon = self ._volume_icons [4 ]
141
165
return volume_icon
142
166
@@ -158,16 +182,20 @@ def wheelEvent(self, event: QWheelEvent):
158
182
elif event .angleDelta ().y () < 0 :
159
183
self ._decrease_volume ()
160
184
185
+
161
186
def toggle_mute (self ):
162
187
current_mute_status = self .volume .GetMute ()
163
188
self .volume .SetMute (not current_mute_status , None )
164
189
self ._update_label ()
165
-
190
+
191
+
166
192
def _initialize_volume_interface (self ):
167
193
CoInitialize ()
168
194
try :
169
195
devices = AudioUtilities .GetSpeakers ()
170
196
interface = devices .Activate (IAudioEndpointVolume ._iid_ , CLSCTX_ALL , None )
171
197
self .volume = interface .QueryInterface (IAudioEndpointVolume )
198
+ self .callback = AudioEndpointVolumeCallback (self )
199
+ self .volume .RegisterControlChangeNotify (self .callback )
172
200
finally :
173
- CoUninitialize ()
201
+ CoUninitialize ()
0 commit comments