diff --git a/.github/workflows/build-and-release.yaml b/.github/workflows/build-and-release.yaml
deleted file mode 100644
index 1d99e8e..0000000
--- a/.github/workflows/build-and-release.yaml
+++ /dev/null
@@ -1,8 +0,0 @@
-name: Build and Release
-
-on:
- workflow_dispatch:
-
-jobs:
- build:
- uses: odizinne/shared-workflows/.github/workflows/build-and-release.yml@main
\ No newline at end of file
diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
deleted file mode 100644
index 280a125..0000000
--- a/.github/workflows/build.yaml
+++ /dev/null
@@ -1,10 +0,0 @@
-name: Build
-
-on:
- push:
- branches:
- - main
-
-jobs:
- build:
- uses: odizinne/shared-workflows/.github/workflows/build.yml@main
\ No newline at end of file
diff --git a/.github/workflows/qt-msvc-build-and-release.yml b/.github/workflows/qt-msvc-build-and-release.yml
new file mode 100644
index 0000000..8b214e4
--- /dev/null
+++ b/.github/workflows/qt-msvc-build-and-release.yml
@@ -0,0 +1,181 @@
+name: Build and Release
+
+on:
+ workflow_dispatch:
+
+jobs:
+ build:
+ runs-on: windows-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Set up Visual Studio shell
+ uses: egor-tensin/vs-shell@v2
+ with:
+ arch: x64
+
+ - name: Install Qt
+ uses: jurplel/install-qt-action@v4
+ with:
+ version: '6.7.2'
+ add-tools-to-path: true
+
+ - name: Install jom
+ id: jom-setup
+ shell: pwsh
+ run: |
+ $url = "https://download.qt.io/official_releases/jom/jom_1_1_4.zip"
+ $outputPath = "jom_1_1_4.zip"
+ Invoke-WebRequest -Uri $url -OutFile $outputPath
+ $extractPath = "jom"
+ if (-not (Test-Path $extractPath)) {
+ New-Item -ItemType Directory -Path $extractPath | Out-Null
+ }
+ Expand-Archive -Path $outputPath -DestinationPath $extractPath
+ $jomDir = "$(pwd)\jom"
+ $jomExe = "$jomDir\jom.exe"
+ if (Test-Path $jomExe) {
+ Write-Output "JOM Path: $jomDir"
+ Write-Output "::set-output name=jom_path::$jomDir"
+ } else {
+ Write-Error "jom.exe not found in $jomDir"
+ exit 1
+ }
+
+ - name: Build with qmake and jom
+ shell: pwsh
+ run: |
+ mkdir build
+ cd build
+ qmake ..\HeadsetControl-Qt.pro CONFIG+=release
+ # Use the JOM path variable
+ $jomPath = "${{ steps.jom-setup.outputs.jom_path }}"
+ & "$jomPath\jom.exe"
+
+ - name: Remove source and object files
+ shell: pwsh
+ run: |
+ # Define the directory
+ $buildDir = "build/release"
+
+ # Check if the directory exists
+ if (Test-Path $buildDir) {
+ # Remove .cpp, .h, .obj, and .res files
+ Get-ChildItem -Path $buildDir -Include *.cpp, *.h, *.obj, *.res -Recurse | Remove-Item -Force
+ } else {
+ Write-Host "Directory not found: $buildDir"
+ }
+
+ - name: Deploy Qt
+ shell: pwsh
+ run: |
+ # Navigate to the directory containing the executable
+ cd build
+
+ # Use the found path to windeployqt
+ $windeployqtPath = "D:\a\HeadsetControl-Qt\Qt\6.7.2\msvc2019_64\bin\windeployqt6.exe"
+
+ # Check if the executable exists
+ if (Test-Path $windeployqtPath) {
+ # Run windeployqt with the updated options
+ & $windeployqtPath `
+ --exclude-plugins qmodernwindowsstyle,qsvgicon,qsvg,qico,qjpeg,qgif,qnetworklistmanager,qtuiotouchplugin `
+ --no-opengl-sw `
+ --no-system-dxc-compiler `
+ --no-compiler-runtime `
+ --no-translations `
+ --no-system-d3d-compiler `
+ D:\a\HeadsetControl-Qt\HeadsetControl-Qt\build\release\HeadsetControl-Qt.exe
+ } else {
+ Write-Error "windeployqt not found at the expected path!"
+ exit 1
+ }
+
+ - name: Rename release folder
+ shell: pwsh
+ run: |
+ $releaseDir = "build/release"
+ $newDir = "HeadsetControl-Qt"
+ if (Test-Path $releaseDir) {
+ Rename-Item -Path $releaseDir -NewName $newDir
+ } else {
+ Write-Error "Release folder not found!"
+ exit 1
+ }
+
+ - name: Zip binaries folder
+ run: |
+ $zipFile = "build/HeadsetControl-Qt_msvc_64.zip"
+ $folder = "build/HeadsetControl-Qt"
+ Compress-Archive -Path $folder -DestinationPath $zipFile
+ shell: pwsh
+
+ - name: Upload Artifacts
+ uses: actions/upload-artifact@v4
+ with:
+ name: HeadsetControl-Qt_msvc_64
+ path: build/HeadsetControl-Qt_msvc_64.zip
+
+ release:
+ runs-on: ubuntu-latest
+ needs: build
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Download artifact zip
+ uses: actions/download-artifact@v4
+ with:
+ name: HeadsetControl-Qt_msvc_64
+
+ - name: List files in current directory
+ run: ls -la
+
+ - name: Bump version and create release
+ id: bump_release
+ run: |
+ git fetch --tags
+
+ # Determine the latest major version tag
+ LAST_MAJOR_TAG=$(git tag --list 'v*.*.*' | sed -E 's/^v?([0-9]+)\..*/\1/' | sort -nr | head -n 1)
+
+ # Increment the major version number
+ if [ -z "$LAST_MAJOR_TAG" ]; then
+ NEW_TAG="v1"
+ else
+ NEW_TAG="v$(($LAST_MAJOR_TAG + 1))"
+ fi
+
+ # Check if the tag already exists
+ if git rev-parse "$NEW_TAG" >/dev/null 2>&1; then
+ echo "Tag '$NEW_TAG' already exists. Incrementing to next major version."
+ LAST_MAJOR_TAG=$(git tag --list 'v*' | sed -E 's/^v?([0-9]+).*/\1/' | sort -nr | head -n 1)
+ NEW_TAG="v$(($LAST_MAJOR_TAG + 1))"
+ fi
+
+ echo "New tag is $NEW_TAG"
+ git tag $NEW_TAG
+ git push origin $NEW_TAG
+ echo "new_tag=$NEW_TAG" >> $GITHUB_ENV
+
+ - name: Create GitHub release
+ id: create_release
+ uses: actions/create-release@v1
+ with:
+ tag_name: ${{ env.new_tag }}
+ release_name: ${{ env.new_tag }}
+ body: ""
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Upload release assets
+ uses: actions/upload-release-asset@v1
+ with:
+ upload_url: ${{ steps.create_release.outputs.upload_url }}
+ asset_path: HeadsetControl-Qt_msvc_64.zip
+ asset_name: HeadsetControl-Qt_msvc_64.zip
+ asset_content_type: application/zip
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/qt-msvc-build.yml b/.github/workflows/qt-msvc-build.yml
new file mode 100644
index 0000000..aab44c8
--- /dev/null
+++ b/.github/workflows/qt-msvc-build.yml
@@ -0,0 +1,121 @@
+name: Build
+
+on:
+ push:
+ branches:
+ - main
+
+jobs:
+ build:
+ runs-on: windows-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Set up Visual Studio shell
+ uses: egor-tensin/vs-shell@v2
+ with:
+ arch: x64
+
+ - name: Install Qt
+ uses: jurplel/install-qt-action@v4
+ with:
+ version: '6.7.2'
+ add-tools-to-path: true
+
+ - name: Install jom
+ id: jom-setup
+ shell: pwsh
+ run: |
+ $url = "https://download.qt.io/official_releases/jom/jom_1_1_4.zip"
+ $outputPath = "jom_1_1_4.zip"
+ Invoke-WebRequest -Uri $url -OutFile $outputPath
+ $extractPath = "jom"
+ if (-not (Test-Path $extractPath)) {
+ New-Item -ItemType Directory -Path $extractPath | Out-Null
+ }
+ Expand-Archive -Path $outputPath -DestinationPath $extractPath
+ $jomDir = "$(pwd)\jom"
+ $jomExe = "$jomDir\jom.exe"
+ if (Test-Path $jomExe) {
+ Write-Output "JOM Path: $jomDir"
+ Write-Output "::set-output name=jom_path::$jomDir"
+ } else {
+ Write-Error "jom.exe not found in $jomDir"
+ exit 1
+ }
+
+ - name: Build with qmake and jom
+ shell: pwsh
+ run: |
+ mkdir build
+ cd build
+ qmake ..\HeadsetControl-Qt.pro CONFIG+=release
+ # Use the JOM path variable
+ $jomPath = "${{ steps.jom-setup.outputs.jom_path }}"
+ & "$jomPath\jom.exe"
+
+ - name: Remove source and object files
+ shell: pwsh
+ run: |
+ # Define the directory
+ $buildDir = "build/release"
+
+ # Check if the directory exists
+ if (Test-Path $buildDir) {
+ # Remove .cpp, .h, .obj, and .res files
+ Get-ChildItem -Path $buildDir -Include *.cpp, *.h, *.obj, *.res -Recurse | Remove-Item -Force
+ } else {
+ Write-Host "Directory not found: $buildDir"
+ }
+
+ - name: Deploy Qt
+ shell: pwsh
+ run: |
+ # Navigate to the directory containing the executable
+ cd build
+
+ # Use the found path to windeployqt
+ $windeployqtPath = "D:\a\HeadsetControl-Qt\Qt\6.7.2\msvc2019_64\bin\windeployqt6.exe"
+
+ # Check if the executable exists
+ if (Test-Path $windeployqtPath) {
+ # Run windeployqt with the updated options
+ & $windeployqtPath `
+ --exclude-plugins qmodernwindowsstyle,qsvgicon,qsvg,qico,qjpeg,qgif,qnetworklistmanager,qtuiotouchplugin `
+ --no-opengl-sw `
+ --no-system-dxc-compiler `
+ --no-compiler-runtime `
+ --no-translations `
+ --no-system-d3d-compiler `
+ D:\a\HeadsetControl-Qt\HeadsetControl-Qt\build\release\HeadsetControl-Qt.exe
+ } else {
+ Write-Error "windeployqt not found at the expected path!"
+ exit 1
+ }
+
+ - name: Rename release folder
+ shell: pwsh
+ run: |
+ $releaseDir = "build/release"
+ $newDir = "HeadsetControl-Qt"
+ if (Test-Path $releaseDir) {
+ Rename-Item -Path $releaseDir -NewName $newDir
+ } else {
+ Write-Error "Release folder not found!"
+ exit 1
+ }
+
+ - name: Zip binaries folder
+ run: |
+ $zipFile = "build/HeadsetControl-Qt_msvc_64.zip"
+ $folder = "build/HeadsetControl-Qt"
+ Compress-Archive -Path $folder -DestinationPath $zipFile
+ shell: pwsh
+
+ - name: Upload Artifacts
+ uses: actions/upload-artifact@v4
+ with:
+ name: HeadsetControl-Qt_msvc_64
+ path: build/HeadsetControl-Qt_msvc_64.zip
diff --git a/.gitignore b/.gitignore
index 99155ae..ae30fc8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,2 @@
build/
-__pycache__/
-.venv/
-.idea/
\ No newline at end of file
+HeadsetControl-Qt.pro.user
\ No newline at end of file
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
deleted file mode 100644
index 54231dc..0000000
--- a/.vscode/tasks.json
+++ /dev/null
@@ -1,25 +0,0 @@
-{
- "version": "2.0.0",
- "tasks": [
- {
- "label": "Build with cx_Freeze",
- "type": "shell",
- "command": "${command:python.interpreterPath}",
- "args": [
- "src/setup.py",
- "build"
- ],
- "group": {
- "kind": "build",
- "isDefault": true
- },
- "presentation": {
- "echo": true,
- "reveal": "always",
- "focus": false,
- "panel": "shared"
- },
- "problemMatcher": []
- }
- ]
-}
diff --git a/HeadsetControl-Qt.pro b/HeadsetControl-Qt.pro
new file mode 100644
index 0000000..d20f727
--- /dev/null
+++ b/HeadsetControl-Qt.pro
@@ -0,0 +1,48 @@
+QT += core gui
+
+greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
+
+CONFIG += c++17
+
+# You can make your code fail to compile if it uses deprecated APIs.
+# In order to do so, uncomment the following line.
+#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
+
+INCLUDEPATH += \
+ src/ShortcutManager \
+ src/HeadsetControlQt \
+ src/Utils
+
+SOURCES += \
+ src/ShortcutManager/shortcutmanager.cpp \
+ src/main.cpp \
+ src/HeadsetControlQt/headsetcontrolqt.cpp \
+ src/Utils/utils.cpp
+
+HEADERS += \
+ src/ShortcutManager/shortcutmanager.h \
+ src/HeadsetControlQt/headsetcontrolqt.h \
+ src/Utils/utils.h
+
+FORMS += \
+ src/HeadsetControlQt/headsetcontrolqt.ui
+
+RESOURCES += \
+ src/Resources/resources.qrc
+
+RC_FILE = src/Resources/appicon.rc
+
+LIBS += -lole32
+
+# Default rules for deployment.
+qnx: target.path = /tmp/$${TARGET}/bin
+else: unix:!android: target.path = /opt/$${TARGET}/bin
+!isEmpty(target.path): INSTALLS += target
+
+# Define the source directory and the target directory in the build folder
+DEPENDENCIES_DIR = $$PWD/src/dependencies
+DEST_DIR = $$OUT_PWD/release/dependencies
+
+QMAKE_POST_LINK += powershell -Command "New-Item -ItemType Directory -Path '$$DEST_DIR' -Force; Copy-Item -Path '$$DEPENDENCIES_DIR\*' -Destination '$$DEST_DIR' -Recurse -Force"
+#mkdir $$DEST_DIR
+
diff --git a/README.md b/README.md
index 29b29b8..e1944c6 100644
--- a/README.md
+++ b/README.md
@@ -2,52 +2,20 @@
[![Github All Releases](https://img.shields.io/github/downloads/odizinne/headsetcontrol-qt/total.svg)]()
[![license](https://img.shields.io/github/license/odizinne/headsetcontrol-qt)]()
-PyQt6 Gui for headsetcontrol.
-Windows / linux compatible.
+Qt Gui for headsetcontrol.
+Features a tray icon with battery status in tooltip.
![image](assets/screenshot.png)
-Features a tray icon with battery status in tooltip.
-Lights state will be restored on next start.
-
-Linux compatibility was designed with plasma system icons in mind.
-Gnome users should have a look at [headsetcontrol-indicator](https://github.com/Odizinne/headsetcontrol-indicator) instead.
-For any other environment, light custom icons will be used by default.
-You can change it in application settings.
-
## Supported devices
-Supported devices list can be found [here](https://github.com/Sapd/HeadsetControl?tab=readme-ov-file#supported-headsets).
+Supported devices list can be found [here](https://github.com/Sapd/HeadsetControl?tab=readme-ov-file#supported-headsets).
+If a particular setting is greyed out on the settings page, it indicates that your device does not support it.
## Download
-Precompiled windows binaries can be found in [release](https://odizinne.net/Odizinne/HeadsetControl-Qt/releases/latest) section
-
-## Build and run
-
-Clone this repository: `git clone https://github.com/Odizinne/HeadsetControl-Qt.git`
-CD to the cloned repo: `cd HeadsetControl-Qt`
-
-**Windows**
-Install dependencies:
-`pip install -r requirements.txt`
-
-Build exe:
-`python .\src\setup.py build`
-
-Install directory and create startup shortcut:
-`python .\src\setup.py install`
-
-**Linux**
-Dependencies:
-- PyQt6 (from package manager is recommended, else use pip)
-- [HeadsetControl](https://github.com/Sapd/HeadsetControl)
-
-There is nothing to build.
-
-Make sure `headsetcontrol-qt.py` is executable: `chmod +x ./headsetcontrol-qt.py`
-Then run it: `headsetcontrol-qt.py`
+Precompiled windows binaries can be found in [release](https://odizinne.net/Odizinne/HeadsetControl-Qt/releases/latest) section
## To-do
- Add other headsetcontrol supported settings (My headset does not support them so i cannot test)
-- Build linux appimage
+- Bring back linux support
diff --git a/assets/screenshot.png b/assets/screenshot.png
index a177c4b..6d4373c 100644
Binary files a/assets/screenshot.png and b/assets/screenshot.png differ
diff --git a/install.sh b/install.sh
deleted file mode 100755
index 4a3b7a2..0000000
--- a/install.sh
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/bin/bash
-
-SRC_DIR="$(dirname "$(realpath "$0")")"
-DEST_DIR="$HOME/.local/bin/headsetcontrol-qt"
-DESKTOP_ENTRY_FILE="$HOME/.local/share/applications/headsetcontrol-qt.desktop"
-
-mkdir -p "$DEST_DIR"
-cp -r "$SRC_DIR/src/"* "$DEST_DIR/"
-
-EXEC_CMD="$DEST_DIR/headsetcontrol-qt.py"
-
-cat > "$DESKTOP_ENTRY_FILE" << EOL
-[Desktop Entry]
-Name=HeadsetControl-Qt
-Exec=$EXEC_CMD
-Icon=$DEST_DIR/icons/icon.png
-Path=$DEST_DIR/
-Terminal=false
-Type=Application
-EOL
-
-chmod +x "$DESKTOP_ENTRY_FILE"
-echo ""
-echo "Setup complete. HeadsetControl-Qt should now be available in your applications menu."
diff --git a/requirements.txt b/requirements.txt
deleted file mode 100644
index 2441df6..0000000
Binary files a/requirements.txt and /dev/null differ
diff --git a/src/HeadsetControlQt/headsetcontrolqt.cpp b/src/HeadsetControlQt/headsetcontrolqt.cpp
new file mode 100644
index 0000000..865dcee
--- /dev/null
+++ b/src/HeadsetControlQt/headsetcontrolqt.cpp
@@ -0,0 +1,433 @@
+#include "HeadsetControlQt.h"
+#include "ui_HeadsetControlQt.h"
+#include "utils.h"
+#include "shortcutmanager.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+const QString HeadsetControlQt::settingsFile = QStandardPaths::writableLocation(
+ QStandardPaths::AppDataLocation)
+ + "/HeadsetControl-Qt/settings.json";
+
+HeadsetControlQt::HeadsetControlQt(QWidget *parent)
+ : QMainWindow(parent)
+ , ui(new Ui::HeadsetControlQt)
+ , trayIcon(new QSystemTrayIcon(this))
+ , timer(new QTimer(this))
+ , firstRun(false)
+{
+ ui->setupUi(this);
+ setWindowTitle("HeadsetControl-Qt");
+ setWindowIcon(QIcon(":/icons/icon.png"));
+ setFixedSize(size());
+ setFont();
+ loadSettings();
+ initUI();
+ populateComboBoxes();
+ createTrayIcon();
+ updateHeadsetInfo();
+ timer->start(10000);
+ checkStartupCheckbox();
+ connect(timer, &QTimer::timeout, this, &HeadsetControlQt::updateHeadsetInfo);
+ if (firstRun) {
+ this->show();
+ }
+}
+
+HeadsetControlQt::~HeadsetControlQt()
+{
+ delete ui;
+}
+
+void HeadsetControlQt::initUI()
+{
+ connect(ui->ledBox, &QCheckBox::stateChanged, this, &HeadsetControlQt::onLedBoxStateChanged);
+ connect(ui->lightBatterySpinbox, QOverload::of(&QSpinBox::valueChanged), this, &HeadsetControlQt::onLightBatterySpinboxValueChanged);
+ connect(ui->notificationBatterySpinbox, QOverload::of(&QSpinBox::valueChanged), this, &HeadsetControlQt::onNotificationBatterySpinboxValueChanged);
+ connect(ui->startupCheckbox, &QCheckBox::stateChanged, this, &HeadsetControlQt::onStartupCheckBoxStateChanged);
+ connect(ui->sidetoneSlider, &QSlider::sliderReleased, this, &HeadsetControlQt::onSidetoneSliderSliderReleased);
+ connect(ui->themeComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &HeadsetControlQt::onThemeComboBoxCurrentIndexChanged);
+}
+
+void HeadsetControlQt::populateComboBoxes()
+{
+ ui->themeComboBox->addItem(tr("System"));
+ ui->themeComboBox->addItem(tr("Dark"));
+ ui->themeComboBox->addItem(tr("Light"));
+}
+
+void HeadsetControlQt::createTrayIcon()
+{
+ trayIcon->setIcon(QIcon(":/icons/icon.png"));
+ QMenu *trayMenu = new QMenu(this);
+ QAction *showAction = new QAction("Show", this);
+ connect(showAction, &QAction::triggered, this, &HeadsetControlQt::toggleWindow);
+ trayMenu->addAction(showAction);
+ QAction *exitAction = new QAction("Exit", this);
+ connect(exitAction, &QAction::triggered, this, &HeadsetControlQt::exitApp);
+ trayMenu->addAction(exitAction);
+ trayIcon->setContextMenu(trayMenu);
+ trayIcon->show();
+ connect(trayIcon, &QSystemTrayIcon::activated, this, &HeadsetControlQt::trayIconActivated);
+}
+
+void HeadsetControlQt::createDefaultSettings()
+{
+ ui->ledBox->setChecked(true);
+ ui->lightBatterySpinbox->setValue(20);
+ ui->notificationBatterySpinbox->setValue(20);
+ ui->sidetoneSlider->setValue(0);
+ ui->themeComboBox->setCurrentIndex(0);
+ saveSettings();
+
+ firstRun = true;
+}
+
+void HeadsetControlQt::loadSettings()
+{
+ QDir settingsDir(QFileInfo(settingsFile).absolutePath());
+ if (!settingsDir.exists()) {
+ settingsDir.mkpath(settingsDir.absolutePath());
+ }
+
+ QFile file(settingsFile);
+ if (!file.exists()) {
+ createDefaultSettings();
+
+ } else {
+ if (file.open(QIODevice::ReadOnly)) {
+ QJsonParseError parseError;
+ QJsonDocument doc = QJsonDocument::fromJson(file.readAll(), &parseError);
+ if (parseError.error == QJsonParseError::NoError) {
+ settings = doc.object();
+ }
+ file.close();
+ }
+ }
+ applySettings();
+}
+
+void HeadsetControlQt::applySettings()
+{
+ ui->ledBox->setChecked(settings.value("led_state").toBool());
+ ui->lightBatterySpinbox->setEnabled(settings.value("led_state").toBool());
+ ui->lightBatteryLabel->setEnabled(settings.value("led_state").toBool());
+ ui->lightBatterySpinbox->setValue(settings.value("light_battery_threshold").toInt());
+ ui->notificationBatterySpinbox->setValue(settings.value("notification_battery_threshold").toInt());
+ ui->sidetoneSlider->setValue(settings.value("sidetone").toInt());
+ ui->themeComboBox->setCurrentIndex(settings.value("theme").toInt());
+ setSidetone();
+ toggleLED();
+}
+
+void HeadsetControlQt::saveSettings()
+{
+ settings["led_state"] = ui->ledBox->isChecked();
+ settings["light_battery_threshold"] = ui->lightBatterySpinbox->value();
+ settings["notification_battery_threshold"] = ui->notificationBatterySpinbox->value();
+ settings["sidetone"] = ui->sidetoneSlider->value();
+ settings["theme"] = ui->themeComboBox->currentIndex();
+
+ QFile file(settingsFile);
+ if (file.open(QIODevice::WriteOnly)) {
+ QJsonDocument doc(settings);
+ file.write(doc.toJson(QJsonDocument::Indented));
+ file.close();
+ }
+}
+
+void HeadsetControlQt::updateHeadsetInfo()
+{
+ QProcess process;
+ process.start("dependencies/headsetcontrol.exe", QStringList() << "-o" << "json");
+
+ if (!process.waitForStarted()) {
+ qDebug() << "Failed to start process:" << process.errorString();
+ return;
+ }
+
+ if (!process.waitForFinished()) {
+ qDebug() << "Process did not finish successfully:" << process.errorString();
+ return;
+ }
+
+ QByteArray output = process.readAllStandardOutput();
+
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(output);
+ if (jsonDoc.isNull()) {
+ return;
+ }
+
+ QJsonObject jsonObj = jsonDoc.object();
+
+ if (jsonObj.contains("devices")) {
+ QJsonArray devicesArray = jsonObj["devices"].toArray();
+ if (devicesArray.size() > 0) {
+ QJsonObject headsetInfo = devicesArray.first().toObject();
+
+ updateUIWithHeadsetInfo(headsetInfo);
+ manageLEDBasedOnBattery(headsetInfo);
+ sendNotificationBasedOnBattery(headsetInfo);
+ } else {
+ qDebug() << "No devices found.";
+ noDeviceFound();
+ }
+ } else {
+ qDebug() << "No 'devices' key found in JSON.";
+ noDeviceFound();
+ }
+}
+
+void HeadsetControlQt::manageLEDBasedOnBattery(const QJsonObject &headsetInfo)
+{
+ if (!ui->ledBox->isChecked()) {
+ return;
+ }
+
+ ui->lightBatterySpinbox->setEnabled(true);
+ ui->lightBatteryLabel->setEnabled(true);
+
+ QJsonObject batteryInfo = headsetInfo["battery"].toObject();
+ int batteryLevel = batteryInfo["level"].toInt();
+ QString batteryStatus = batteryInfo["status"].toString();
+ bool available = (batteryStatus == "BATTERY_AVAILABLE");
+
+ if (batteryLevel < ui->lightBatterySpinbox->value() && ledState && available) {
+ toggleLED();
+ ledState = false;
+ saveSettings();
+ } else if (batteryLevel >= ui->lightBatterySpinbox->value() + 5 && !ledState && available) {
+ toggleLED();
+ ledState = true;
+ saveSettings();
+ }
+}
+
+void HeadsetControlQt::sendNotificationBasedOnBattery(const QJsonObject &headsetInfo)
+{
+ QJsonObject batteryInfo = headsetInfo["battery"].toObject();
+ QString headsetName = headsetInfo["device"].toString();
+ int batteryLevel = batteryInfo["level"].toInt();
+ QString batteryStatus = batteryInfo["status"].toString();
+ bool available = (batteryStatus == "BATTERY_AVAILABLE");
+
+ if (batteryLevel < ui->notificationBatterySpinbox->value() && !notificationSent && available) {
+ sendNotification("Low battery", QString("%1 has %2% battery left.").arg(headsetName).arg(batteryLevel), QIcon(":/icons/icon.png"), 3000);
+ notificationSent = true;
+ } else if (batteryLevel >= ui->notificationBatterySpinbox->value() + 5 && notificationSent && available) {
+ notificationSent = false;
+ }
+}
+
+void HeadsetControlQt::sendNotification(const QString &title, const QString &message, const QIcon &icon, int duration)
+{
+ trayIcon->showMessage(title, message, icon, duration);
+}
+
+void HeadsetControlQt::toggleLED()
+{
+ QProcess process;
+ process.start("dependencies/headsetcontrol.exe", QStringList() << "-l" << (ui->ledBox->isChecked() ? "1" : "0"));
+ process.waitForFinished();
+}
+
+void HeadsetControlQt::updateUIWithHeadsetInfo(const QJsonObject &headsetInfo)
+{
+ QString deviceName = headsetInfo["device"].toString();
+ QStringList capabilities = headsetInfo["capabilities_str"].toVariant().toStringList();
+ QJsonObject batteryInfo = headsetInfo["battery"].toObject();
+
+ ui->deviceGroupBox->setTitle(deviceName);
+
+ QString batteryStatus = batteryInfo["status"].toString();
+ int batteryLevel = batteryInfo["level"].toInt();
+
+ if (batteryStatus == "BATTERY_AVAILABLE") {
+ ui->batteryBar->setValue(batteryLevel);
+ ui->batteryBar->setFormat(QString::number(batteryLevel) + "%");
+ trayIcon->setToolTip(QString("Battery Level: %1%").arg(batteryLevel));
+
+ QString iconPath = getBatteryIcon(batteryLevel, false, false);
+ trayIcon->setIcon(QIcon(iconPath));
+ } else if (batteryStatus == "BATTERY_CHARGING") {
+ ui->batteryBar->setValue(0);
+ ui->batteryBar->setFormat("Charging");
+ trayIcon->setToolTip("Battery Charging");
+
+ QString iconPath = getBatteryIcon(batteryLevel, true, false);
+ trayIcon->setIcon(QIcon(iconPath));
+ } else {
+ ui->batteryBar->setValue(0);
+ ui->batteryBar->setFormat("Off");
+ trayIcon->setToolTip("Battery Unavailable");
+
+ QString iconPath = getBatteryIcon(batteryLevel, false, true);
+ trayIcon->setIcon(QIcon(iconPath));
+ }
+
+ ui->ledBox->setEnabled(capabilities.contains("lights"));
+ ui->ledLabel->setEnabled(capabilities.contains("lights"));
+
+ ui->sidetoneSlider->setEnabled(capabilities.contains("sidetone"));
+ ui->sidetoneLabel->setEnabled(capabilities.contains("sidetone"));
+
+ toggleUIElements(true);
+}
+
+QString HeadsetControlQt::getBatteryIcon(int batteryLevel, bool charging, bool missing)
+{
+ QString theme;
+ int themeIndex = ui->themeComboBox->currentIndex();
+ if (themeIndex == 0) {
+ theme = getTheme();
+ } else if (themeIndex == 1) {
+ theme = "light";
+ } else if (themeIndex == 2) {
+ theme = "dark";
+ }
+
+ QString iconName;
+ if (missing) {
+ iconName = QString("battery-missing-%1").arg(theme);
+ } else if (charging) {
+ iconName = "battery-100-charging-" + theme;
+ } else {
+ if (batteryLevel >= 90) iconName = "battery-100-" + theme;
+ else if (batteryLevel >= 80) iconName = "battery-090-" + theme;
+ else if (batteryLevel >= 70) iconName = "battery-080-" + theme;
+ else if (batteryLevel >= 60) iconName = "battery-070-" + theme;
+ else if (batteryLevel >= 50) iconName = "battery-060-" + theme;
+ else if (batteryLevel >= 40) iconName = "battery-050-" + theme;
+ else if (batteryLevel >= 30) iconName = "battery-040-" + theme;
+ else if (batteryLevel >= 20) iconName = "battery-030-" + theme;
+ else if (batteryLevel >= 10) iconName = "battery-020-" + theme;
+ else iconName = "battery-010-" + theme;
+ }
+
+ return QString(":/icons/%1.png").arg(iconName);
+}
+
+void HeadsetControlQt::noDeviceFound()
+{
+ toggleUIElements(false);
+ trayIcon->setToolTip("No Device Found");
+}
+
+void HeadsetControlQt::toggleUIElements(bool show)
+{
+ ui->deviceGroupBox->setVisible(show);
+ ui->generalGroupBox->setVisible(show);
+ ui->notFoundLabel->setVisible(!show);
+ this->setMinimumSize(0, 0);
+ this->adjustSize();
+}
+
+void HeadsetControlQt::onLedBoxStateChanged()
+{
+ toggleLED();
+ ui->lightBatterySpinbox->setEnabled(ui->ledBox->isChecked());
+ ui->lightBatteryLabel->setEnabled(ui->ledBox->isChecked());
+ saveSettings();
+}
+
+void HeadsetControlQt::onLightBatterySpinboxValueChanged(int value)
+{
+ saveSettings();
+}
+
+void HeadsetControlQt::onNotificationBatterySpinboxValueChanged(int value)
+{
+ saveSettings();
+}
+
+void HeadsetControlQt::onStartupCheckBoxStateChanged()
+{
+ if (ui->startupCheckbox->isChecked()) {
+ manageShortcut(true);
+ } else {
+ manageShortcut(false);
+ }
+}
+
+void HeadsetControlQt::onSidetoneSliderSliderReleased()
+{
+ setSidetone();
+ saveSettings();
+}
+
+void HeadsetControlQt::onThemeComboBoxCurrentIndexChanged(int index)
+{
+ updateHeadsetInfo();
+ saveSettings();
+}
+
+void HeadsetControlQt::setSidetone()
+{
+ QProcess process;
+ process.start("dependencies/headsetcontrol.exe", QStringList() << "-s" << QString::number(ui->sidetoneSlider->value()));
+ process.waitForFinished();
+}
+
+void HeadsetControlQt::toggleWindow()
+{
+ if (isVisible()) {
+ hide();
+ trayIcon->contextMenu()->actions().first()->setText("Show");
+ } else {
+ show();
+ trayIcon->contextMenu()->actions().first()->setText("Hide");
+ }
+}
+
+void HeadsetControlQt::trayIconActivated(QSystemTrayIcon::ActivationReason reason)
+{
+ if (reason == QSystemTrayIcon::ActivationReason::Trigger) {
+ toggleWindow();
+ }
+}
+
+void HeadsetControlQt::exitApp()
+{
+ trayIcon->hide();
+ QApplication::quit();
+}
+
+void HeadsetControlQt::checkStartupCheckbox()
+{
+ if (isShortcutPresent()) {
+ ui->startupCheckbox->setChecked(true);
+ }
+}
+
+void HeadsetControlQt::closeEvent(QCloseEvent *event)
+{
+ event->ignore();
+ hide();
+}
+
+
+void HeadsetControlQt::setFont()
+{
+ QList groupBoxes = {
+ ui->deviceGroupBox,
+ ui->generalGroupBox
+ };
+
+ for (QGroupBox* groupBox : groupBoxes) {
+ groupBox->setStyleSheet("font-weight: bold;");
+
+ const QList children = groupBox->findChildren();
+ for (QWidget* child : children) {
+ child->setStyleSheet("font-weight: normal;");
+ }
+ }
+}
diff --git a/src/HeadsetControlQt/headsetcontrolqt.h b/src/HeadsetControlQt/headsetcontrolqt.h
new file mode 100644
index 0000000..d35e132
--- /dev/null
+++ b/src/HeadsetControlQt/headsetcontrolqt.h
@@ -0,0 +1,75 @@
+#ifndef HEADSETCONTROLQT_H
+#define HEADSETCONTROLQT_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+QT_BEGIN_NAMESPACE
+namespace Ui {
+class HeadsetControlQt;
+}
+QT_END_NAMESPACE
+
+class HeadsetControlQt : public QMainWindow
+{
+ Q_OBJECT
+
+public:
+ explicit HeadsetControlQt(QWidget *parent = nullptr);
+ ~HeadsetControlQt();
+
+private slots:
+ void onLedBoxStateChanged();
+ void onLightBatterySpinboxValueChanged(int value);
+ void onNotificationBatterySpinboxValueChanged(int value);
+ void onSidetoneSliderSliderReleased();
+ void onThemeComboBoxCurrentIndexChanged(int index);
+ void updateHeadsetInfo();
+ void toggleWindow();
+ void trayIconActivated(QSystemTrayIcon::ActivationReason reason);
+ void exitApp();
+ void checkStartupCheckbox();
+ void onStartupCheckBoxStateChanged();
+
+private:
+ void initUI();
+ void populateComboBoxes();
+ void createTrayIcon();
+ void loadSettings();
+ void applySettings();
+ void saveSettings();
+ void createDefaultSettings();
+ void manageLEDBasedOnBattery(const QJsonObject &headsetInfo);
+ void sendNotificationBasedOnBattery(const QJsonObject &headsetInfo);
+ void sendNotification(const QString &title, const QString &message, const QIcon &icon, int duration);
+ void toggleLED();
+ void setSidetone();
+ void setFont();
+ void updateUIWithHeadsetInfo(const QJsonObject &headsetInfo);
+ QString getBatteryIcon(int batteryLevel, bool charging = false, bool missing = false);
+ void noDeviceFound();
+ void toggleUIElements(bool show);
+ static const QString settingsFile;
+
+ Ui::HeadsetControlQt *ui;
+ QSystemTrayIcon *trayIcon;
+ QTimer *timer;
+ QJsonObject settings;
+ bool ledState;
+ bool notificationSent;
+ bool firstRun;
+
+protected:
+ void closeEvent(QCloseEvent *event) override;
+};
+
+#endif // HEADSETCONTROLQT_H
diff --git a/src/designer/ui_mainwindow.ui b/src/HeadsetControlQt/headsetcontrolqt.ui
similarity index 100%
rename from src/designer/ui_mainwindow.ui
rename to src/HeadsetControlQt/headsetcontrolqt.ui
diff --git a/src/Resources/appicon.rc b/src/Resources/appicon.rc
new file mode 100644
index 0000000..dfccaf7
--- /dev/null
+++ b/src/Resources/appicon.rc
@@ -0,0 +1,3 @@
+#include
+
+IDI_ICON1 ICON "icons/icon.ico"
diff --git a/src/icons/battery-010-dark.png b/src/Resources/icons/battery-010-dark.png
similarity index 100%
rename from src/icons/battery-010-dark.png
rename to src/Resources/icons/battery-010-dark.png
diff --git a/src/icons/battery-010-light.png b/src/Resources/icons/battery-010-light.png
similarity index 100%
rename from src/icons/battery-010-light.png
rename to src/Resources/icons/battery-010-light.png
diff --git a/src/icons/battery-020-dark.png b/src/Resources/icons/battery-020-dark.png
similarity index 100%
rename from src/icons/battery-020-dark.png
rename to src/Resources/icons/battery-020-dark.png
diff --git a/src/icons/battery-020-light.png b/src/Resources/icons/battery-020-light.png
similarity index 100%
rename from src/icons/battery-020-light.png
rename to src/Resources/icons/battery-020-light.png
diff --git a/src/icons/battery-030-dark.png b/src/Resources/icons/battery-030-dark.png
similarity index 100%
rename from src/icons/battery-030-dark.png
rename to src/Resources/icons/battery-030-dark.png
diff --git a/src/icons/battery-030-light.png b/src/Resources/icons/battery-030-light.png
similarity index 100%
rename from src/icons/battery-030-light.png
rename to src/Resources/icons/battery-030-light.png
diff --git a/src/icons/battery-040-dark.png b/src/Resources/icons/battery-040-dark.png
similarity index 100%
rename from src/icons/battery-040-dark.png
rename to src/Resources/icons/battery-040-dark.png
diff --git a/src/icons/battery-040-light.png b/src/Resources/icons/battery-040-light.png
similarity index 100%
rename from src/icons/battery-040-light.png
rename to src/Resources/icons/battery-040-light.png
diff --git a/src/icons/battery-050-dark.png b/src/Resources/icons/battery-050-dark.png
similarity index 100%
rename from src/icons/battery-050-dark.png
rename to src/Resources/icons/battery-050-dark.png
diff --git a/src/icons/battery-050-light.png b/src/Resources/icons/battery-050-light.png
similarity index 100%
rename from src/icons/battery-050-light.png
rename to src/Resources/icons/battery-050-light.png
diff --git a/src/icons/battery-060-dark.png b/src/Resources/icons/battery-060-dark.png
similarity index 100%
rename from src/icons/battery-060-dark.png
rename to src/Resources/icons/battery-060-dark.png
diff --git a/src/icons/battery-060-light.png b/src/Resources/icons/battery-060-light.png
similarity index 100%
rename from src/icons/battery-060-light.png
rename to src/Resources/icons/battery-060-light.png
diff --git a/src/icons/battery-070-dark.png b/src/Resources/icons/battery-070-dark.png
similarity index 100%
rename from src/icons/battery-070-dark.png
rename to src/Resources/icons/battery-070-dark.png
diff --git a/src/icons/battery-070-light.png b/src/Resources/icons/battery-070-light.png
similarity index 100%
rename from src/icons/battery-070-light.png
rename to src/Resources/icons/battery-070-light.png
diff --git a/src/icons/battery-080-dark.png b/src/Resources/icons/battery-080-dark.png
similarity index 100%
rename from src/icons/battery-080-dark.png
rename to src/Resources/icons/battery-080-dark.png
diff --git a/src/icons/battery-080-light.png b/src/Resources/icons/battery-080-light.png
similarity index 100%
rename from src/icons/battery-080-light.png
rename to src/Resources/icons/battery-080-light.png
diff --git a/src/icons/battery-090-dark.png b/src/Resources/icons/battery-090-dark.png
similarity index 100%
rename from src/icons/battery-090-dark.png
rename to src/Resources/icons/battery-090-dark.png
diff --git a/src/icons/battery-090-light.png b/src/Resources/icons/battery-090-light.png
similarity index 100%
rename from src/icons/battery-090-light.png
rename to src/Resources/icons/battery-090-light.png
diff --git a/src/icons/battery-100-charging-dark.png b/src/Resources/icons/battery-100-charging-dark.png
similarity index 100%
rename from src/icons/battery-100-charging-dark.png
rename to src/Resources/icons/battery-100-charging-dark.png
diff --git a/src/icons/battery-100-charging-light.png b/src/Resources/icons/battery-100-charging-light.png
similarity index 100%
rename from src/icons/battery-100-charging-light.png
rename to src/Resources/icons/battery-100-charging-light.png
diff --git a/src/icons/battery-100-dark.png b/src/Resources/icons/battery-100-dark.png
similarity index 100%
rename from src/icons/battery-100-dark.png
rename to src/Resources/icons/battery-100-dark.png
diff --git a/src/icons/battery-100-light.png b/src/Resources/icons/battery-100-light.png
similarity index 100%
rename from src/icons/battery-100-light.png
rename to src/Resources/icons/battery-100-light.png
diff --git a/src/icons/battery-missing-dark.png b/src/Resources/icons/battery-missing-dark.png
similarity index 100%
rename from src/icons/battery-missing-dark.png
rename to src/Resources/icons/battery-missing-dark.png
diff --git a/src/icons/battery-missing-light.png b/src/Resources/icons/battery-missing-light.png
similarity index 100%
rename from src/icons/battery-missing-light.png
rename to src/Resources/icons/battery-missing-light.png
diff --git a/src/icons/icon.ico b/src/Resources/icons/icon.ico
similarity index 100%
rename from src/icons/icon.ico
rename to src/Resources/icons/icon.ico
diff --git a/src/icons/icon.png b/src/Resources/icons/icon.png
similarity index 100%
rename from src/icons/icon.png
rename to src/Resources/icons/icon.png
diff --git a/src/Resources/resources.qrc b/src/Resources/resources.qrc
new file mode 100644
index 0000000..5808dd8
--- /dev/null
+++ b/src/Resources/resources.qrc
@@ -0,0 +1,30 @@
+
+
+ icons/battery-010-dark.png
+ icons/battery-020-dark.png
+ icons/battery-030-dark.png
+ icons/battery-040-dark.png
+ icons/battery-050-dark.png
+ icons/battery-060-dark.png
+ icons/battery-070-dark.png
+ icons/battery-080-dark.png
+ icons/battery-090-dark.png
+ icons/battery-100-dark.png
+ icons/battery-100-charging-dark.png
+ icons/battery-missing-dark.png
+ icons/battery-010-light.png
+ icons/battery-020-light.png
+ icons/battery-030-light.png
+ icons/battery-040-light.png
+ icons/battery-050-light.png
+ icons/battery-060-light.png
+ icons/battery-070-light.png
+ icons/battery-080-light.png
+ icons/battery-090-light.png
+ icons/battery-100-light.png
+ icons/battery-100-charging-light.png
+ icons/battery-missing-light.png
+ icons/icon.ico
+ icons/icon.png
+
+
diff --git a/src/ShortcutManager/shortcutmanager.cpp b/src/ShortcutManager/shortcutmanager.cpp
new file mode 100644
index 0000000..e943fb4
--- /dev/null
+++ b/src/ShortcutManager/shortcutmanager.cpp
@@ -0,0 +1,103 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+QString getStartupFolder()
+{
+ QString path;
+ WCHAR szPath[MAX_PATH];
+ if (SHGetFolderPath(NULL, CSIDL_STARTUP, NULL, 0, szPath) == S_OK) {
+ path = QString::fromWCharArray(szPath);
+ }
+ return path;
+}
+
+void setPaths(QString &targetPath, QString &startupFolder)
+{
+ TCHAR executablePath[MAX_PATH];
+ GetModuleFileName(NULL, executablePath, MAX_PATH);
+ targetPath = QString::fromWCharArray(executablePath);
+
+ startupFolder = getStartupFolder();
+}
+
+QString getShortcutPath()
+{
+ static QString shortcutName = "HeadsetControl-Qt.lnk";
+ return getStartupFolder() + "\\" + shortcutName;
+}
+
+void createShortcut(const QString &targetPath)
+{
+ QString shortcutPath = getShortcutPath();
+ QString workingDirectory = QFileInfo(targetPath).path();
+
+ IShellLink *pShellLink = nullptr;
+ IPersistFile *pPersistFile = nullptr;
+
+ if (FAILED(CoInitialize(nullptr))) {
+ qDebug() << "Failed to initialize COM library.";
+ return;
+ }
+
+ if (SUCCEEDED(CoCreateInstance(CLSID_ShellLink,
+ nullptr,
+ CLSCTX_INPROC_SERVER,
+ IID_IShellLink,
+ (void **) &pShellLink))) {
+ pShellLink->SetPath(targetPath.toStdWString().c_str());
+ pShellLink->SetWorkingDirectory(workingDirectory.toStdWString().c_str());
+ pShellLink->SetDescription(L"Launch HeadsetControl-Qt");
+
+ if (SUCCEEDED(pShellLink->QueryInterface(IID_IPersistFile, (void **) &pPersistFile))) {
+ pPersistFile->Save(shortcutPath.toStdWString().c_str(), TRUE);
+ pPersistFile->Release();
+ }
+
+ pShellLink->Release();
+ } else {
+ qDebug() << "Failed to create ShellLink instance.";
+ }
+
+ CoUninitialize();
+}
+
+bool isShortcutPresent()
+{
+ QString shortcutPath = getShortcutPath();
+ return QFile::exists(shortcutPath);
+}
+
+void removeShortcut()
+{
+ QString shortcutPath = getShortcutPath();
+ if (isShortcutPresent()) {
+ QFile::remove(shortcutPath);
+ }
+}
+
+void manageShortcut(bool state)
+{
+ QString targetPath;
+ QString startupFolder;
+
+ setPaths(targetPath, startupFolder);
+
+ if (state) {
+ if (!isShortcutPresent()) {
+ createShortcut(targetPath);
+ }
+ } else {
+ if (isShortcutPresent()) {
+ removeShortcut();
+ }
+ }
+}
diff --git a/src/ShortcutManager/shortcutmanager.h b/src/ShortcutManager/shortcutmanager.h
new file mode 100644
index 0000000..03dc509
--- /dev/null
+++ b/src/ShortcutManager/shortcutmanager.h
@@ -0,0 +1,9 @@
+#ifndef SHORTCUTMANAGER_H
+#define SHORTCUTMANAGER_H
+
+#include
+
+bool isShortcutPresent();
+void manageShortcut(bool state);
+
+#endif // SHORTCUTMANAGER_H
diff --git a/src/Utils/utils.cpp b/src/Utils/utils.cpp
new file mode 100644
index 0000000..81aae56
--- /dev/null
+++ b/src/Utils/utils.cpp
@@ -0,0 +1,32 @@
+#include "utils.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+
+QString getTheme()
+{
+ // Determine the theme based on registry value
+ QSettings settings(
+ "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize",
+ QSettings::NativeFormat);
+ int value = settings.value("AppsUseLightTheme", 1).toInt();
+
+ // Return the opposite to match icon (dark icon on light theme)
+ return (value == 0) ? "light" : "dark";
+}
+
+QIcon getIconForTheme()
+{
+ QString theme = getTheme();
+ QString iconPath = QString(":/icons/icon_%1.png").arg(theme);
+ return QIcon(iconPath);
+}
+
diff --git a/src/Utils/utils.h b/src/Utils/utils.h
new file mode 100644
index 0000000..d517977
--- /dev/null
+++ b/src/Utils/utils.h
@@ -0,0 +1,9 @@
+#ifndef UTILS_H
+#define UTILS_H
+#include
+#include
+
+QString getTheme();
+QIcon getIconForTheme();
+
+#endif // UTILS_H
diff --git a/src/headsetcontrol-qt.py b/src/headsetcontrol-qt.py
deleted file mode 100644
index 4eee2c0..0000000
--- a/src/headsetcontrol-qt.py
+++ /dev/null
@@ -1,410 +0,0 @@
-#!/usr/bin/env python3
-
-import sys
-import subprocess
-import json
-import os
-import winreg
-from PyQt6.QtWidgets import QApplication, QMainWindow, QSystemTrayIcon, QMenu
-from PyQt6.QtGui import QIcon, QAction
-from PyQt6.QtCore import QTimer, QTranslator, QLocale
-from ui_mainwindow import Ui_HeadsetControlQt
-
-if sys.platform == "win32":
- import winshell
-
- SETTINGS_DIR = os.path.join(os.getenv("APPDATA"), "HeadsetControl-Qt")
- HEADSETCONTROL_EXECUTABLE = os.path.join("dependencies", "headsetcontrol.exe")
- STARTUP_FOLDER = winshell.startup()
-else:
- SETTINGS_DIR = os.path.join(os.path.expanduser("~"), ".config", "HeadsetControl-Qt")
- HEADSETCONTROL_EXECUTABLE = "headsetcontrol"
- DESKTOP_FILE_PATH = os.path.join(os.path.expanduser("~"), ".config", "autostart", "headsetcontrol-qt.desktop")
-
-ICONS_DIR = os.path.join("icons")
-APP_ICON = os.path.join(ICONS_DIR, "icon.png")
-SETTINGS_FILE = os.path.join(SETTINGS_DIR, "settings.json")
-
-
-def is_dark_mode_enabled():
- registry_key = winreg.OpenKey(
- winreg.HKEY_CURRENT_USER, r"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize"
- )
- value, regtype = winreg.QueryValueEx(registry_key, "AppsUseLightTheme")
- winreg.CloseKey(registry_key)
- return value == 0
-
-
-class HeadsetControlApp(QMainWindow):
- def __init__(self):
- super().__init__()
- self.timer = None
- self.tray_icon = None
- self.ui = Ui_HeadsetControlQt()
- self.ui.setupUi(self)
- self.setWindowTitle("HeadsetControl-Qt")
- self.setWindowIcon(QIcon(APP_ICON))
- self.setFixedSize(self.size())
- self.led_state = None
- self.notification_sent = False
- self.init_ui()
- self.create_tray_icon()
- self.load_settings()
- self.update_headset_info()
- self.init_timer()
- self.check_startup_checkbox()
- self.set_sidetone()
- self.on_ledBox_state_changed()
-
- def init_ui(self):
- self.ui.ledBox.stateChanged.connect(self.on_ledBox_state_changed)
- self.ui.lightBatterySpinbox.valueChanged.connect(self.save_settings)
- self.ui.notificationBatterySpinbox.valueChanged.connect(self.save_settings)
- self.ui.startupCheckbox.stateChanged.connect(self.on_startup_checkbox_state_changed)
- self.ui.sidetoneSlider.sliderReleased.connect(self.set_sidetone)
- self.ui.themeComboBox.addItem(self.tr("System"))
- self.ui.themeComboBox.addItem(self.tr("Light"))
- self.ui.themeComboBox.addItem(self.tr("Dark"))
- self.ui.themeComboBox.currentIndexChanged.connect(self.on_themeComboBox_index_changed)
-
- def create_tray_icon(self):
- self.tray_icon = QSystemTrayIcon(self)
- self.tray_icon.setIcon(QIcon(APP_ICON))
- tray_menu = QMenu(self)
- show_action = QAction("Show", self)
- show_action.triggered.connect(self.toggle_window)
- tray_menu.addAction(show_action)
- exit_action = QAction("Exit", self)
- exit_action.triggered.connect(self.exit_app)
- tray_menu.addAction(exit_action)
- self.tray_icon.setContextMenu(tray_menu)
- self.tray_icon.show()
- self.tray_icon.activated.connect(self.tray_icon_activated)
-
- def tray_icon_activated(self, reason):
- if reason == QSystemTrayIcon.ActivationReason.Trigger:
- self.toggle_window()
-
- def toggle_window(self):
- if self.isVisible():
- self.hide()
- self.tray_icon.contextMenu().actions()[0].setText("Show")
- else:
- self.show()
- self.tray_icon.contextMenu().actions()[0].setText("Hide")
-
- def init_timer(self):
- self.timer = QTimer(self)
- self.timer.timeout.connect(self.update_headset_info)
- self.timer.start(10000)
-
- def load_settings(self):
- if not os.path.exists(SETTINGS_FILE):
- os.makedirs(SETTINGS_DIR, exist_ok=True)
- self.create_default_settings()
- with open(SETTINGS_FILE, "r") as f:
- settings = json.load(f)
- self.led_state = settings.get("led_state", True)
- self.ui.ledBox.setChecked(self.led_state)
- self.ui.lightBatterySpinbox.setEnabled(self.led_state)
- self.ui.lightBatteryLabel.setEnabled(self.led_state)
- self.ui.lightBatterySpinbox.setValue(settings.get("light_battery_threshold", 20))
- self.ui.notificationBatterySpinbox.setValue(settings.get("notification_battery_threshold", 20))
- self.ui.sidetoneSlider.setValue(settings.get("sidetone", 0))
- self.ui.themeComboBox.setCurrentIndex(settings.get("theme", 0))
-
- def save_settings(self):
- settings = {
- "led_state": self.ui.ledBox.isChecked(),
- "light_battery_threshold": self.ui.lightBatterySpinbox.value(),
- "notification_battery_threshold": self.ui.notificationBatterySpinbox.value(),
- "sidetone": self.ui.sidetoneSlider.value(),
- "theme": self.ui.themeComboBox.currentIndex(),
- }
- with open(SETTINGS_FILE, "w") as f:
- json.dump(settings, f, indent=4)
-
- def create_default_settings(self):
- settings = {
- "led_state": True,
- "light_battery_threshold": 20,
- "notification_battery_threshold": 20,
- "sidetone": 0,
- "theme": 0,
- }
- with open(SETTINGS_FILE, "w") as f:
- json.dump(settings, f, indent=4)
-
- def update_headset_info(self):
- command = [HEADSETCONTROL_EXECUTABLE, "-o", "json"]
- creation_flags = subprocess.CREATE_NO_WINDOW if sys.platform == "win32" else 0
- result = subprocess.Popen(
- command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, creationflags=creation_flags
- )
- stdout, stderr = result.communicate(timeout=10)
-
- if result.returncode == 0:
- data = json.loads(stdout)
- if "devices" in data and len(data["devices"]) > 0:
- headset_info = data["devices"][0]
- self.update_ui_with_headset_info(headset_info)
- self.manage_led_based_on_battery(headset_info)
- self.send_notification_based_on_battery(headset_info)
- else:
- self.no_device_found()
- else:
- print("Error running headsetcontrol:", stderr)
- self.no_device_found()
-
- def manage_led_based_on_battery(self, headset_info):
- if not self.ui.ledBox.isChecked():
- return
-
- self.ui.lightBatterySpinbox.setEnabled(True)
- self.ui.lightBatteryLabel.setEnabled(True)
- battery_info = headset_info.get("battery", {})
- battery_level = battery_info.get("level", 0)
- battery_status = battery_info.get("status", "UNKNOWN")
- available = battery_status == "BATTERY_AVAILABLE"
-
- if battery_level < self.ui.lightBatterySpinbox.value() and self.led_state and available:
- self.toggle_led(False)
- self.led_state = False
- self.save_settings()
- elif battery_level >= self.ui.lightBatterySpinbox.value() + 5 and not self.led_state and available:
- self.toggle_led(True)
- self.led_state = True
- self.save_settings()
-
- def send_notification_based_on_battery(self, headset_info):
- battery_info = headset_info.get("battery", {})
- headset_name = headset_info.get("device", "Unknown Device")
- battery_level = battery_info.get("level", 0)
- battery_status = battery_info.get("status", "UNKNOWN")
- available = battery_status == "BATTERY_AVAILABLE"
-
- if battery_level < self.ui.notificationBatterySpinbox.value() and not self.notification_sent and available:
- self.send_notification(
- "Low battery", f"{headset_name} has {battery_level}% battery left.", QIcon("icons/icon.png"), 3000
- )
- self.notification_sent = True
- elif battery_level >= self.ui.notificationBatterySpinbox.value() + 5 and self.notification_sent and available:
- self.notification_sent = False
-
- def send_notification(self, title, message, icon, duration):
- self.tray_icon.showMessage(title, message, icon, duration)
-
- def toggle_led(self, state):
- command = [HEADSETCONTROL_EXECUTABLE, "-l", "1" if state else "0"]
- creation_flags = subprocess.CREATE_NO_WINDOW if sys.platform == "win32" else 0
- subprocess.Popen(
- command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, creationflags=creation_flags
- )
-
- def update_ui_with_headset_info(self, headset_info):
- device_name = headset_info.get("device", "Unknown Device")
- capabilities = headset_info.get("capabilities_str", [])
- battery_info = headset_info.get("battery", {})
-
- self.ui.deviceGroupBox.setTitle(f"{device_name}")
-
- battery_status = battery_info.get("status", "UNKNOWN")
- if battery_status == "BATTERY_AVAILABLE":
- battery_level = battery_info.get("level", 0)
- self.ui.batteryBar.setValue(battery_level)
- self.ui.batteryBar.setFormat(f"{battery_level}%")
- self.tray_icon.setToolTip(f"Battery Level: {battery_level}%")
-
- icon_path = self.get_battery_icon(battery_level, charging=False)
- elif battery_status == "BATTERY_CHARGING":
- self.ui.batteryBar.setValue(0)
- self.ui.batteryBar.setFormat("Charging")
- self.tray_icon.setToolTip("Battery Charging")
-
- icon_path = self.get_battery_icon(battery_level=None, charging=True)
- else:
- self.ui.batteryBar.setValue(0)
- self.ui.batteryBar.setFormat("Off")
- self.tray_icon.setToolTip("Battery Unavailable")
-
- icon_path = self.get_battery_icon(battery_level=None, missing=True)
-
- if sys.platform == "win32":
- self.tray_icon.setIcon(QIcon(icon_path))
- elif sys.platform == "linux":
- self.tray_icon.setIcon(QIcon.fromTheme(icon_path))
-
- if "lights" in capabilities:
- self.ui.ledBox.setEnabled(True)
- self.ui.ledLabel.setEnabled(True)
- else:
- self.ui.ledBox.setEnabled(False)
- self.ui.ledLabel.setEnabled(False)
-
- if "sidetone" in capabilities:
- self.ui.sidetoneSlider.setEnabled(True)
- self.ui.sidetoneLabel.setEnabled(True)
- else:
- self.ui.sidetoneSlider.setEnabled(False)
- self.ui.sidetoneLabel.setEnabled(False)
-
- self.toggle_ui_elements(True)
-
- def get_battery_icon(self, battery_level, charging=False, missing=False):
- theme = None
- if self.ui.themeComboBox.currentIndex() == 0:
- if sys.platform == "win32":
- dark_mode = is_dark_mode_enabled()
- theme = "light" if dark_mode else "dark"
- elif sys.platform == "linux":
- if os.getenv("XDG_CURRENT_DESKTOP") == "KDE":
- theme = "symbolic"
- else:
- # I cannot detect every desktop and settings, so assume user is using dark theme and use light icons
- theme = "light"
- elif self.ui.themeComboBox.currentIndex() == 1:
- theme = "light"
- elif self.ui.themeComboBox.currentIndex() == 2:
- theme = "dark"
-
- if missing:
- icon_name = f"battery-missing-{theme}"
- elif charging:
- icon_name = f"battery-100-charging-{theme}"
- else:
- if battery_level is not None:
- battery_levels = {
- 90: "100",
- 80: "090",
- 70: "080",
- 60: "070",
- 50: "060",
- 40: "050",
- 30: "040",
- 20: "030",
- 10: "020",
- 0: "010",
- }
- icon_name = None
- for level, percentage in battery_levels.items():
- if battery_level >= level:
- icon_name = f"battery-{percentage}-{theme}"
- break
- else:
- icon_name = f"battery-missing-{theme}"
-
- if sys.platform == "win32":
- icon_name += ".png"
- icon_path = os.path.join(ICONS_DIR, icon_name)
- return icon_path
- elif sys.platform == "linux":
- icon_path = icon_name
- return icon_path
-
- def no_device_found(self):
- self.toggle_ui_elements(False)
- self.tray_icon.setToolTip("No Device Found")
-
- def on_ledBox_state_changed(self):
- lights = True if self.ui.ledBox.isChecked() else False
- self.toggle_led(lights)
-
- self.ui.lightBatterySpinbox.setEnabled(True if self.ui.ledBox.isChecked() else False)
- self.ui.lightBatteryLabel.setEnabled(True if self.ui.ledBox.isChecked() else False)
- self.save_settings()
-
- def on_themeComboBox_index_changed(self):
- self.update_headset_info()
- self.save_settings()
-
- def set_sidetone(self):
- sidetone_value = self.ui.sidetoneSlider.value()
- command = [HEADSETCONTROL_EXECUTABLE, "-s", str(sidetone_value)]
- creation_flags = subprocess.CREATE_NO_WINDOW if sys.platform == "win32" else 0
- subprocess.Popen(
- command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, creationflags=creation_flags
- )
- self.save_settings()
-
- def toggle_ui_elements(self, show):
- self.ui.deviceGroupBox.setVisible(show)
- self.ui.generalGroupBox.setVisible(show)
- self.ui.notFoundLabel.setVisible(not show)
- self.setMinimumSize(0, 0)
- self.adjustSize()
- self.setFixedSize(self.size())
-
- def show_window(self):
- self.show()
-
- def exit_app(self):
- self.tray_icon.hide()
- QApplication.quit()
-
- def closeEvent(self, event):
- event.ignore()
- self.hide()
-
- def on_startup_checkbox_state_changed(self):
- checked = self.ui.startupCheckbox.isChecked()
-
- if sys.platform == "win32":
- shortcut_path = os.path.join(winshell.startup(), "HeadsetControl-Qt.lnk")
- target_path = sys.executable
- working_directory = os.path.dirname(target_path)
-
- if checked:
- winshell.CreateShortcut(
- Path=shortcut_path,
- Target=target_path,
- Icon=(target_path, 0),
- Description="Launch HeadsetControl-Qt",
- StartIn=working_directory,
- )
- else:
- if os.path.exists(shortcut_path):
- os.remove(shortcut_path)
-
- elif sys.platform == "linux":
- if checked:
- if not os.path.exists(os.path.dirname(DESKTOP_FILE_PATH)):
- os.makedirs(os.path.dirname(DESKTOP_FILE_PATH))
-
- script_folder = os.path.dirname(__file__)
- desktop_entry_content = (
- "[Desktop Entry]\n"
- f"Path={script_folder}\n"
- "Type=Application\n"
- f"Exec={sys.executable} {__file__}\n"
- "Name=HeadsetControl-Qt\n"
- )
- with open(DESKTOP_FILE_PATH, "w") as f:
- f.write(desktop_entry_content)
- else:
- if os.path.exists(DESKTOP_FILE_PATH):
- os.remove(DESKTOP_FILE_PATH)
-
- def check_startup_checkbox(self):
- if sys.platform == "win32":
- shortcut_path = os.path.join(winshell.startup(), "HeadsetControl-Qt.lnk")
- self.ui.startupCheckbox.setChecked(os.path.exists(shortcut_path))
- elif sys.platform == "linux":
- self.ui.startupCheckbox.setChecked(os.path.exists(DESKTOP_FILE_PATH))
-
-
-if __name__ == "__main__":
- app = QApplication(sys.argv)
- translator = QTranslator()
- locale_name = QLocale.system().name()
- locale = locale_name[:2]
- if locale:
- file_name = f"tr/headsetcontrol-qt_{locale}.qm"
- else:
- file_name = None
-
- if file_name and translator.load(file_name):
- app.installTranslator(translator)
-
- app.setStyle("fusion")
- window = HeadsetControlApp()
- sys.exit(app.exec())
diff --git a/src/main.cpp b/src/main.cpp
new file mode 100644
index 0000000..cc10573
--- /dev/null
+++ b/src/main.cpp
@@ -0,0 +1,11 @@
+#include "headsetcontrolqt.h"
+
+#include
+
+int main(int argc, char *argv[])
+{
+ QApplication a(argc, argv);
+ a.setStyle("fusion");
+ HeadsetControlQt w;
+ return a.exec();
+}
diff --git a/src/setup.py b/src/setup.py
deleted file mode 100644
index 92bf030..0000000
--- a/src/setup.py
+++ /dev/null
@@ -1,34 +0,0 @@
-import os
-from cx_Freeze import setup, Executable
-
-src_dir = os.path.dirname(os.path.abspath(__file__))
-build_dir = "build/HeadsetControl-Qt"
-install_dir = os.path.join(os.getenv("LOCALAPPDATA"), "programs", "HeadsetControl-Qt")
-
-include_files = [os.path.join(src_dir, "dependencies/"), os.path.join(src_dir, "icons"), os.path.join(src_dir, "tr/")]
-
-zip_include_packages = ["PyQt6"]
-
-build_exe_options = {
- "build_exe": build_dir,
- "include_files": include_files,
- "zip_include_packages": zip_include_packages,
- "excludes": ["tkinter"],
- "silent": True,
-}
-
-executables = [
- Executable(
- script=os.path.join(src_dir, "headsetcontrol-qt.py"),
- base="Win32GUI",
- icon=os.path.join(src_dir, "icons/icon.ico"),
- target_name="HeadsetControl-Qt",
- )
-]
-
-setup(
- name="HeadsetControl-Qt",
- version="1.0",
- options={"build_exe": build_exe_options},
- executables=executables,
-)
diff --git a/src/tr/headsetcontrol-qt_de.qm b/src/tr/headsetcontrol-qt_de.qm
deleted file mode 100644
index be651ee..0000000
--- a/src/tr/headsetcontrol-qt_de.qm
+++ /dev/null
@@ -1 +0,0 @@
-<¸dÊÍ!¿`¡½Ý
\ No newline at end of file
diff --git a/src/tr/headsetcontrol-qt_de.ts b/src/tr/headsetcontrol-qt_de.ts
deleted file mode 100644
index eb0cccf..0000000
--- a/src/tr/headsetcontrol-qt_de.ts
+++ /dev/null
@@ -1,80 +0,0 @@
-
-
-
-
- HeadsetControlApp
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- HeadsetControlQt
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/tr/headsetcontrol-qt_en.qm b/src/tr/headsetcontrol-qt_en.qm
deleted file mode 100644
index fb6e67e..0000000
Binary files a/src/tr/headsetcontrol-qt_en.qm and /dev/null differ
diff --git a/src/tr/headsetcontrol-qt_en.ts b/src/tr/headsetcontrol-qt_en.ts
deleted file mode 100644
index a4742c4..0000000
--- a/src/tr/headsetcontrol-qt_en.ts
+++ /dev/null
@@ -1,80 +0,0 @@
-
-
-
-
- HeadsetControlApp
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- HeadsetControlQt
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/tr/headsetcontrol-qt_es.qm b/src/tr/headsetcontrol-qt_es.qm
deleted file mode 100644
index be651ee..0000000
--- a/src/tr/headsetcontrol-qt_es.qm
+++ /dev/null
@@ -1 +0,0 @@
-<¸dÊÍ!¿`¡½Ý
\ No newline at end of file
diff --git a/src/tr/headsetcontrol-qt_es.ts b/src/tr/headsetcontrol-qt_es.ts
deleted file mode 100644
index eb0cccf..0000000
--- a/src/tr/headsetcontrol-qt_es.ts
+++ /dev/null
@@ -1,80 +0,0 @@
-
-
-
-
- HeadsetControlApp
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- HeadsetControlQt
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/tr/headsetcontrol-qt_fr.qm b/src/tr/headsetcontrol-qt_fr.qm
deleted file mode 100644
index 387c50d..0000000
Binary files a/src/tr/headsetcontrol-qt_fr.qm and /dev/null differ
diff --git a/src/tr/headsetcontrol-qt_fr.ts b/src/tr/headsetcontrol-qt_fr.ts
deleted file mode 100644
index c171fd5..0000000
--- a/src/tr/headsetcontrol-qt_fr.ts
+++ /dev/null
@@ -1,84 +0,0 @@
-
-
-
-
- HeadsetControlApp
-
-
-
- Système
-
-
-
-
- Clair
-
-
-
-
- Sombre
-
-
-
- HeadsetControlQt
-
-
-
-
-
-
-
-
-
-
-
-
-
- Batterie
-
-
-
-
- Retour voix
-
-
-
-
- LEDs
-
-
-
-
- Désactiver les LEDs quand la batterie tombe sous:
-
-
-
-
- Notifier quand la batterie tombe sous
-
-
-
-
- Paramètres
-
-
-
-
- Thème des icones
-
-
-
-
- Lancer au démarrage
-
-
-
-
- Aucun casque détécté.
-
-
-
- Paramètres
-
-
-
diff --git a/src/ui_mainwindow.py b/src/ui_mainwindow.py
deleted file mode 100644
index f26ef2f..0000000
--- a/src/ui_mainwindow.py
+++ /dev/null
@@ -1,154 +0,0 @@
-# Form implementation generated from reading ui file '.\designer\ui_mainwindow.ui'
-#
-# Created by: PyQt6 UI code generator 6.7.0
-#
-# WARNING: Any manual changes made to this file will be lost when pyuic6 is
-# run again. Do not edit this file unless you know what you are doing.
-
-
-from PyQt6 import QtCore, QtGui, QtWidgets
-
-
-class Ui_HeadsetControlQt(object):
- def setupUi(self, HeadsetControlQt):
- HeadsetControlQt.setObjectName("HeadsetControlQt")
- HeadsetControlQt.resize(433, 351)
- sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Minimum)
- sizePolicy.setHorizontalStretch(0)
- sizePolicy.setVerticalStretch(0)
- sizePolicy.setHeightForWidth(HeadsetControlQt.sizePolicy().hasHeightForWidth())
- HeadsetControlQt.setSizePolicy(sizePolicy)
- self.centralwidget = QtWidgets.QWidget(parent=HeadsetControlQt)
- self.centralwidget.setObjectName("centralwidget")
- self.gridLayout_2 = QtWidgets.QGridLayout(self.centralwidget)
- self.gridLayout_2.setContentsMargins(9, 9, 9, 9)
- self.gridLayout_2.setObjectName("gridLayout_2")
- self.deviceGroupBox = QtWidgets.QGroupBox(parent=self.centralwidget)
- font = QtGui.QFont()
- font.setBold(True)
- self.deviceGroupBox.setFont(font)
- self.deviceGroupBox.setObjectName("deviceGroupBox")
- self.gridLayout_4 = QtWidgets.QGridLayout(self.deviceGroupBox)
- self.gridLayout_4.setObjectName("gridLayout_4")
- self.gridLayout_7 = QtWidgets.QGridLayout()
- self.gridLayout_7.setSpacing(9)
- self.gridLayout_7.setObjectName("gridLayout_7")
- self.batteryLabel = QtWidgets.QLabel(parent=self.deviceGroupBox)
- self.batteryLabel.setMinimumSize(QtCore.QSize(0, 25))
- self.batteryLabel.setObjectName("batteryLabel")
- self.gridLayout_7.addWidget(self.batteryLabel, 0, 0, 1, 1)
- self.batteryBar = QtWidgets.QProgressBar(parent=self.deviceGroupBox)
- self.batteryBar.setMinimumSize(QtCore.QSize(0, 25))
- self.batteryBar.setProperty("value", 50)
- self.batteryBar.setTextVisible(True)
- self.batteryBar.setObjectName("batteryBar")
- self.gridLayout_7.addWidget(self.batteryBar, 0, 1, 1, 1)
- self.sidetoneLabel = QtWidgets.QLabel(parent=self.deviceGroupBox)
- self.sidetoneLabel.setMinimumSize(QtCore.QSize(0, 25))
- self.sidetoneLabel.setObjectName("sidetoneLabel")
- self.gridLayout_7.addWidget(self.sidetoneLabel, 1, 0, 1, 1)
- self.sidetoneSlider = QtWidgets.QSlider(parent=self.deviceGroupBox)
- self.sidetoneSlider.setMinimumSize(QtCore.QSize(0, 25))
- self.sidetoneSlider.setMaximum(128)
- self.sidetoneSlider.setOrientation(QtCore.Qt.Orientation.Horizontal)
- self.sidetoneSlider.setObjectName("sidetoneSlider")
- self.gridLayout_7.addWidget(self.sidetoneSlider, 1, 1, 1, 1)
- self.gridLayout_4.addLayout(self.gridLayout_7, 0, 0, 1, 3)
- self.ledLabel = QtWidgets.QLabel(parent=self.deviceGroupBox)
- self.ledLabel.setMinimumSize(QtCore.QSize(0, 25))
- self.ledLabel.setObjectName("ledLabel")
- self.gridLayout_4.addWidget(self.ledLabel, 1, 0, 1, 2)
- self.ledBox = QtWidgets.QCheckBox(parent=self.deviceGroupBox)
- self.ledBox.setMinimumSize(QtCore.QSize(0, 25))
- self.ledBox.setLayoutDirection(QtCore.Qt.LayoutDirection.RightToLeft)
- self.ledBox.setText("")
- self.ledBox.setChecked(True)
- self.ledBox.setTristate(False)
- self.ledBox.setObjectName("ledBox")
- self.gridLayout_4.addWidget(self.ledBox, 1, 2, 1, 1)
- self.lightBatteryLabel = QtWidgets.QLabel(parent=self.deviceGroupBox)
- self.lightBatteryLabel.setMinimumSize(QtCore.QSize(0, 25))
- self.lightBatteryLabel.setObjectName("lightBatteryLabel")
- self.gridLayout_4.addWidget(self.lightBatteryLabel, 2, 0, 1, 1)
- spacerItem = QtWidgets.QSpacerItem(0, 25, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
- self.gridLayout_4.addItem(spacerItem, 2, 1, 1, 1)
- self.lightBatterySpinbox = QtWidgets.QSpinBox(parent=self.deviceGroupBox)
- self.lightBatterySpinbox.setMinimumSize(QtCore.QSize(100, 25))
- self.lightBatterySpinbox.setFrame(True)
- self.lightBatterySpinbox.setMinimum(0)
- self.lightBatterySpinbox.setMaximum(100)
- self.lightBatterySpinbox.setProperty("value", 20)
- self.lightBatterySpinbox.setObjectName("lightBatterySpinbox")
- self.gridLayout_4.addWidget(self.lightBatterySpinbox, 2, 2, 1, 1)
- self.notification_label = QtWidgets.QLabel(parent=self.deviceGroupBox)
- self.notification_label.setMinimumSize(QtCore.QSize(0, 25))
- self.notification_label.setObjectName("notification_label")
- self.gridLayout_4.addWidget(self.notification_label, 3, 0, 1, 1)
- self.notificationBatterySpinbox = QtWidgets.QSpinBox(parent=self.deviceGroupBox)
- self.notificationBatterySpinbox.setMinimumSize(QtCore.QSize(100, 25))
- self.notificationBatterySpinbox.setFrame(True)
- self.notificationBatterySpinbox.setMinimum(0)
- self.notificationBatterySpinbox.setProperty("value", 20)
- self.notificationBatterySpinbox.setObjectName("notificationBatterySpinbox")
- self.gridLayout_4.addWidget(self.notificationBatterySpinbox, 3, 2, 1, 1)
- self.gridLayout_2.addWidget(self.deviceGroupBox, 0, 0, 1, 1)
- self.generalGroupBox = QtWidgets.QGroupBox(parent=self.centralwidget)
- self.generalGroupBox.setMinimumSize(QtCore.QSize(0, 0))
- font = QtGui.QFont()
- font.setBold(True)
- self.generalGroupBox.setFont(font)
- self.generalGroupBox.setLayoutDirection(QtCore.Qt.LayoutDirection.LeftToRight)
- self.generalGroupBox.setAutoFillBackground(False)
- self.generalGroupBox.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeading|QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
- self.generalGroupBox.setObjectName("generalGroupBox")
- self.gridLayout_3 = QtWidgets.QGridLayout(self.generalGroupBox)
- self.gridLayout_3.setObjectName("gridLayout_3")
- self.label = QtWidgets.QLabel(parent=self.generalGroupBox)
- self.label.setMinimumSize(QtCore.QSize(0, 25))
- self.label.setObjectName("label")
- self.gridLayout_3.addWidget(self.label, 0, 0, 1, 1)
- spacerItem1 = QtWidgets.QSpacerItem(0, 25, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
- self.gridLayout_3.addItem(spacerItem1, 0, 1, 1, 1)
- self.themeComboBox = QtWidgets.QComboBox(parent=self.generalGroupBox)
- self.themeComboBox.setMinimumSize(QtCore.QSize(100, 25))
- self.themeComboBox.setObjectName("themeComboBox")
- self.gridLayout_3.addWidget(self.themeComboBox, 0, 2, 1, 1)
- self.startupLabel = QtWidgets.QLabel(parent=self.generalGroupBox)
- self.startupLabel.setMinimumSize(QtCore.QSize(0, 25))
- self.startupLabel.setObjectName("startupLabel")
- self.gridLayout_3.addWidget(self.startupLabel, 1, 0, 1, 1)
- self.startupCheckbox = QtWidgets.QCheckBox(parent=self.generalGroupBox)
- self.startupCheckbox.setMinimumSize(QtCore.QSize(0, 25))
- self.startupCheckbox.setLayoutDirection(QtCore.Qt.LayoutDirection.RightToLeft)
- self.startupCheckbox.setText("")
- self.startupCheckbox.setObjectName("startupCheckbox")
- self.gridLayout_3.addWidget(self.startupCheckbox, 1, 2, 1, 1)
- self.gridLayout_2.addWidget(self.generalGroupBox, 1, 0, 1, 1)
- self.notFoundLabel = QtWidgets.QLabel(parent=self.centralwidget)
- sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Fixed)
- sizePolicy.setHorizontalStretch(0)
- sizePolicy.setVerticalStretch(0)
- sizePolicy.setHeightForWidth(self.notFoundLabel.sizePolicy().hasHeightForWidth())
- self.notFoundLabel.setSizePolicy(sizePolicy)
- self.notFoundLabel.setMinimumSize(QtCore.QSize(0, 25))
- self.notFoundLabel.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
- self.notFoundLabel.setObjectName("notFoundLabel")
- self.gridLayout_2.addWidget(self.notFoundLabel, 2, 0, 1, 1)
- HeadsetControlQt.setCentralWidget(self.centralwidget)
-
- self.retranslateUi(HeadsetControlQt)
- QtCore.QMetaObject.connectSlotsByName(HeadsetControlQt)
-
- def retranslateUi(self, HeadsetControlQt):
- _translate = QtCore.QCoreApplication.translate
- HeadsetControlQt.setWindowTitle(_translate("HeadsetControlQt", "HeadsetControl-Qt"))
- self.deviceGroupBox.setTitle(_translate("HeadsetControlQt", "GroupBox"))
- self.batteryLabel.setText(_translate("HeadsetControlQt", "Battery"))
- self.sidetoneLabel.setText(_translate("HeadsetControlQt", "Sidetone"))
- self.ledLabel.setText(_translate("HeadsetControlQt", "Lights"))
- self.lightBatteryLabel.setText(_translate("HeadsetControlQt", "Disable lights when battery goes below:"))
- self.notification_label.setText(_translate("HeadsetControlQt", "Send notification when battery goes below"))
- self.generalGroupBox.setTitle(_translate("HeadsetControlQt", "Settings"))
- self.label.setText(_translate("HeadsetControlQt", "Icon theme"))
- self.startupLabel.setText(_translate("HeadsetControlQt", "Run at startup"))
- self.notFoundLabel.setText(_translate("HeadsetControlQt", "No supported headset found."))
diff --git a/tr_script.py b/tr_script.py
deleted file mode 100644
index c600392..0000000
--- a/tr_script.py
+++ /dev/null
@@ -1,63 +0,0 @@
-import argparse
-import subprocess
-
-
-PROJECT = "headsetcontrol-qt"
-
-
-def run_pylupdate():
- try:
- subprocess.run(
- [
- "pylupdate6.exe",
- f"./src/{PROJECT}.py",
- "./src/designer/ui_mainwindow.ui",
- "-ts",
- f"./src/tr/{PROJECT}_fr.ts",
- "-ts",
- f"./src/tr/{PROJECT}_de.ts",
- "-ts",
- f"./src/tr/{PROJECT}_es.ts",
- "-ts",
- f"./src/tr/{PROJECT}_en.ts",
- ],
- check=True,
- )
- print("pylupdate6 executed successfully.")
- except subprocess.CalledProcessError as e:
- print(f"Error running pylupdate6: {e}")
-
-
-def run_lrelease():
- try:
- subprocess.run(
- [
- "lrelease.exe",
- f"./src/tr/{PROJECT}_de.ts",
- f"./src/tr/{PROJECT}_en.ts",
- f"./src/tr/{PROJECT}_es.ts",
- f"./src/tr/{PROJECT}_fr.ts",
- ],
- check=True,
- )
- print("lrelease executed successfully.")
- except subprocess.CalledProcessError as e:
- print(f"Error running lrelease: {e}")
-
-
-def main():
- parser = argparse.ArgumentParser(description="Run pylupdate6 or lrelease.")
- group = parser.add_mutually_exclusive_group(required=True)
- group.add_argument("--generate", "-g", action="store_true", help="Run pylupdate6")
- group.add_argument("--compile", "-c", action="store_true", help="Run lrelease")
-
- args = parser.parse_args()
-
- if args.generate:
- run_pylupdate()
- elif args.compile:
- run_lrelease()
-
-
-if __name__ == "__main__":
- main()