Skip to content

Commit ce697c7

Browse files
committed
Refactor: Add Windows support for Debug Console (using threads) and PyInstaller frozen paths
1 parent 5a6651f commit ce697c7

4 files changed

Lines changed: 55 additions & 25 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
A lightweight, modern tray indicator that shows your **wireless headset’s battery level**, **charging status**, and lets you **control LEDs and sidetone** — all powered by [HeadsetControl](https://github.com/Sapd/HeadsetControl).
88

9-
It uses **PySide6 (Qt)** for the graphical interface and works seamlessly with **KDE Plasma**, **GNOME**, **XFCE**, **Cinnamon**, and other Linux desktop environments.
9+
It uses **PySide6 (Qt)** for the graphical interface and works seamlessly with **KDE Plasma**, **GNOME**, **XFCE**, **Cinnamon**, and other Linux desktop environments ans also macos and Windows.
1010

1111
![Screenshot of the tray icon](screenshot.png)
1212

headset_battery_indicator.py

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@
88
import os
99
import logging
1010
import shutil
11+
import threading
1112
from logging.handlers import RotatingFileHandler
12-
from PySide6.QtCore import QTimer, QSettings, QSocketNotifier
13+
from PySide6.QtCore import QTimer, QSettings, QSocketNotifier , Signal, Slot
1314
from PySide6.QtGui import QIcon, QAction, QActionGroup
1415
from PySide6.QtWidgets import QApplication, QSystemTrayIcon, QMenu
1516

@@ -61,12 +62,18 @@ def setup_logging(debug_to_console=False):
6162
# --- END LOGGING SETUP ---
6263

6364
class HeadsetBatteryTray(QSystemTrayIcon):
65+
command_received = Signal(str)
6466
def __init__(self, debug_mode=False, parent=None):
6567
super().__init__(parent)
6668
self.debug_mode = debug_mode
67-
base_path = os.path.dirname(os.path.abspath(__file__))
69+
70+
if getattr(sys, 'frozen', False):
71+
base_path = sys._MEIPASS
72+
else:
73+
base_path = os.path.dirname(os.path.abspath(__file__))
74+
6875
self.icons_dir = os.path.join(base_path, 'icons')
69-
# Reconfigure logger for console output if in debug mode
76+
7077
if debug_mode:
7178
global logger
7279
logger = setup_logging(debug_to_console=True)
@@ -77,10 +84,8 @@ def __init__(self, debug_mode=False, parent=None):
7784
self.settings = QSettings()
7885
self.load_settings()
7986

80-
# Initialize and set headsetcontrol_path using robust search
8187
self.headsetcontrol_path = self._find_headsetcontrol()
8288

83-
# B. DEPENDENCY CHECK AND ERROR MESSAGE
8489
if not self.headsetcontrol_path:
8590
logger.critical("HeadsetControl binary not found. Functionality disabled.")
8691
self.send_notification(
@@ -99,7 +104,6 @@ def __init__(self, debug_mode=False, parent=None):
99104
self.timer = QTimer(self)
100105
self.timer.timeout.connect(self.update_status)
101106

102-
# Only start timer if binary is found
103107
if self.headsetcontrol_path:
104108
self.timer.start(UPDATE_INTERVAL_MS)
105109

@@ -118,8 +122,18 @@ def __init__(self, debug_mode=False, parent=None):
118122
print(" exit (quits the application)")
119123
print("------------------------------------")
120124

121-
self.stdin_notifier = QSocketNotifier(sys.stdin.fileno(), QSocketNotifier.Type.Read, self)
122-
self.stdin_notifier.activated.connect(self.handle_debug_command)
125+
self.command_received.connect(self.handle_debug_command)
126+
127+
def console_listener():
128+
while True:
129+
try:
130+
line = sys.stdin.readline()
131+
if line:
132+
self.command_received.emit(line.strip())
133+
except ValueError:
134+
break
135+
t = threading.Thread(target=console_listener, daemon=True)
136+
t.start()
123137

124138
def _find_headsetcontrol(self):
125139
"""Searches for the headsetcontrol binary using AppImage logic and system PATH."""
@@ -374,10 +388,11 @@ def apply_saved_settings(self):
374388

375389

376390
# --- Debug Command Handler ---
377-
def handle_debug_command(self):
378-
"""Processes commands from stdin in debug mode."""
391+
@Slot(str)
392+
def handle_debug_command(self, line):
393+
"""Processes commands received from the console thread."""
379394
try:
380-
line = sys.stdin.readline().strip()
395+
# We use now 'line' that is an argument
381396
if not line: return
382397
parts = line.split()
383398
if not parts: return

headset_battery_indicator/__main__.py

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@
88
import os
99
import logging
1010
import shutil
11+
import threading
1112
from logging.handlers import RotatingFileHandler
12-
from PySide6.QtCore import QTimer, QSettings, QSocketNotifier
13+
from PySide6.QtCore import QTimer, QSettings, QSocketNotifier , Signal, Slot
1314
from PySide6.QtGui import QIcon, QAction, QActionGroup
1415
from PySide6.QtWidgets import QApplication, QSystemTrayIcon, QMenu
1516

@@ -61,12 +62,18 @@ def setup_logging(debug_to_console=False):
6162
# --- END LOGGING SETUP ---
6263

6364
class HeadsetBatteryTray(QSystemTrayIcon):
65+
command_received = Signal(str)
6466
def __init__(self, debug_mode=False, parent=None):
6567
super().__init__(parent)
6668
self.debug_mode = debug_mode
67-
base_path = os.path.dirname(os.path.abspath(__file__))
69+
70+
if getattr(sys, 'frozen', False):
71+
base_path = sys._MEIPASS
72+
else:
73+
base_path = os.path.dirname(os.path.abspath(__file__))
74+
6875
self.icons_dir = os.path.join(base_path, 'icons')
69-
# Reconfigure logger for console output if in debug mode
76+
7077
if debug_mode:
7178
global logger
7279
logger = setup_logging(debug_to_console=True)
@@ -77,10 +84,8 @@ def __init__(self, debug_mode=False, parent=None):
7784
self.settings = QSettings()
7885
self.load_settings()
7986

80-
# Initialize and set headsetcontrol_path using robust search
8187
self.headsetcontrol_path = self._find_headsetcontrol()
8288

83-
# B. DEPENDENCY CHECK AND ERROR MESSAGE
8489
if not self.headsetcontrol_path:
8590
logger.critical("HeadsetControl binary not found. Functionality disabled.")
8691
self.send_notification(
@@ -99,7 +104,6 @@ def __init__(self, debug_mode=False, parent=None):
99104
self.timer = QTimer(self)
100105
self.timer.timeout.connect(self.update_status)
101106

102-
# Only start timer if binary is found
103107
if self.headsetcontrol_path:
104108
self.timer.start(UPDATE_INTERVAL_MS)
105109

@@ -118,8 +122,18 @@ def __init__(self, debug_mode=False, parent=None):
118122
print(" exit (quits the application)")
119123
print("------------------------------------")
120124

121-
self.stdin_notifier = QSocketNotifier(sys.stdin.fileno(), QSocketNotifier.Type.Read, self)
122-
self.stdin_notifier.activated.connect(self.handle_debug_command)
125+
self.command_received.connect(self.handle_debug_command)
126+
127+
def console_listener():
128+
while True:
129+
try:
130+
line = sys.stdin.readline()
131+
if line:
132+
self.command_received.emit(line.strip())
133+
except ValueError:
134+
break
135+
t = threading.Thread(target=console_listener, daemon=True)
136+
t.start()
123137

124138
def _find_headsetcontrol(self):
125139
"""Searches for the headsetcontrol binary using AppImage logic and system PATH."""
@@ -374,10 +388,11 @@ def apply_saved_settings(self):
374388

375389

376390
# --- Debug Command Handler ---
377-
def handle_debug_command(self):
378-
"""Processes commands from stdin in debug mode."""
391+
@Slot(str)
392+
def handle_debug_command(self, line):
393+
"""Processes commands received from the console thread."""
379394
try:
380-
line = sys.stdin.readline().strip()
395+
# We use now 'line' that is an argument
381396
if not line: return
382397
parts = line.split()
383398
if not parts: return

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "headset-battery-indicator"
7-
version = "1.4.1"
7+
version = "1.4.2"
88
authors = [
99
{ name = "Ruflas", email = "ruflas@ruflas.dev" },
1010
]
@@ -31,7 +31,7 @@ headset-battery-indicator = "headset_battery_indicator.__main__:main"
3131
[tool.briefcase]
3232
project_name = "Headset Battery Indicator"
3333
bundle = "dev.ruflas"
34-
version = "1.4.1"
34+
version = "1.4.2"
3535
app_name = "headset_battery_indicator"
3636

3737
# Define la aplicación

0 commit comments

Comments
 (0)