20241030191200

This commit is contained in:
zhaoyafan 2024-10-30 19:12:24 +08:00
parent ae38969f30
commit fd1605c246
1 changed files with 170 additions and 9 deletions

179
main.py
View File

@ -1,16 +1,19 @@
import os
import re
import io
import sys
import ctypes
import logging
import threading
import time
import json
import hashlib
import tempfile
import warnings
import subprocess
import win32com.client
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QDialog, QLabel, QComboBox, QCheckBox, QLineEdit, QAction, QMenu, QMessageBox, QPushButton, QTableWidget, QVBoxLayout, QHBoxLayout, QTableWidgetItem
from PyQt5.QtCore import Qt, QCoreApplication, QTimer
from PyQt5.QtCore import Qt, QCoreApplication, QPropertyAnimation, QTimer
from PyQt5.QtGui import QFont
@ -257,7 +260,7 @@ class BtSdk:
return self.dll.Btsdk_IsBluetoothReady()
def enableDeviceDiscovery(self):
device_class, max_dev_num, max_durations = 0X000400, 30, 7
device_class, max_dev_num, max_durations = 0X000400, 30, 10
code = self.dll.Btsdk_StartDeviceDiscovery(device_class, max_dev_num, max_durations)
code != 0 and warnings.warn(self.errs[code])
return not code
@ -376,6 +379,18 @@ class BtSdk:
self.dll.Btsdk_DeleteUnpairedDevicesByClass(0)
return True
def isBluetoothActive(self):
data = []
enum_handle = self.dll.Btsdk_StartEnumConnection()
while True:
conn = self.dll.Btsdk_EnumConnection(enum_handle, 0)
if (not conn) == 1:
self.dll.Btsdk_EndEnumConnection(enum_handle)
break
data.append(conn)
return True if len(data) > 0 else False
def _fd(f):
return f.fileno() if hasattr(f, 'fileno') else f
@ -473,6 +488,23 @@ else:
return fcntl.flock(_fd(f), flags) == 0
def getJson(file, data=None):
if os.path.exists(file):
try:
return json.loads(open(file=file, mode='r', encoding='utf-8').read())
except json.decoder.JSONDecodeError:
return data
return data
def putJson(file, data=None):
with open(file=file, mode='w', encoding='utf-8') as f:
flock(f, LOCK_EX)
res = f.write(json.dumps(data, indent=4, ensure_ascii=True))
flock(f, LOCK_UN)
return res
class LoggerFileHandler:
def __init__(self, log_file: str, mode: str = 'a', level: str = None, fmt: str = None):
self.log, self.mod = log_file, mode
@ -549,6 +581,22 @@ class Logger:
self.c = self.logger.critical
class Setting(dict):
def __init__(self, setting_file: str, setting_default: dict):
self.data_file = os.path.abspath(setting_file)
super().__init__(getJson(self.data_file, setting_default))
def __getitem__(self, item):
if item in self:
return super().__getitem__(item)
else:
return None
def __setitem__(self, key, value):
super().__setitem__(key, value)
putJson(self.data_file, self)
class CustomLineEdit(QLineEdit):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@ -612,11 +660,14 @@ class MainWindow(QMainWindow):
def __init__(self, logger: Logger):
super().__init__()
self.app_name = '蓝牙音频连接'
self.app_version = ('1.0.3', '20240815', 'zhaoyafan', 'zhaoyafan@foxmail.com', 'https://www.fanscloud.net/')
self.app_version = ('1.1.0', '20241030', 'zhaoyafan', 'zhaoyafan@foxmail.com', 'https://www.fanscloud.net/')
self.app_text = {'search': '正在搜索', 'connecting': '连接中...'}
self.logger = logger
self.toast = ToastNotification()
self.setting = Setting(os.path.abspath(os.path.join(tempfile.gettempdir(), '%s.config' % self.md5(__file__)[:16])), {})
self.bluesoleil = None
self.currentDevHandle = None
self.currentDevRssi = 0
self.setWindowTitle(self.app_name)
self.setGeometry(0, 0, 600, 400)
self.statusBar()
@ -652,6 +703,12 @@ class MainWindow(QMainWindow):
self.edtBtName.setFixedSize(250, 36)
m_layout_1.addWidget(self.edtBtName)
# 信号强度
self.edtBtRssi = CustomLineEditNoPopup('')
self.edtBtRssi.setReadOnly(True)
self.edtBtRssi.setFixedSize(38, 36)
m_layout_1.addWidget(self.edtBtRssi)
# 断开连接
self.butDisconnect = CustomPushButton('断开连接')
self.butDisconnect.setFixedSize(64, 36)
@ -673,7 +730,7 @@ class MainWindow(QMainWindow):
# 地址输入
self.edtBtAddr = CustomLineEdit('')
self.edtBtAddr.setStyleSheet('QLineEdit {font-size: 24px; font-family: \'Microsoft YaHei\'; color: #000000; background-color: #FFFFEE;}')
self.edtBtAddr.setFixedSize(249, 36)
self.edtBtAddr.setFixedSize(294, 36)
self.edtBtAddr.returnPressed.connect(self.on_connect_bt_address)
m_layout_3.addWidget(self.edtBtAddr)
@ -694,6 +751,15 @@ class MainWindow(QMainWindow):
self.show()
self.init_bluesoleil()
self.connect_status_update_timer = QTimer()
self.connect_status_update_timer.timeout.connect(self.connect_status_update)
if (self.bluesoleil.isBluetoothActive() and self.setting['deviceHandle'] > 0) == 1:
self.in_connect_process()
self.currentDevHandle = self.setting['deviceHandle'] or 0
self.currentDevRssi = self.setting['deviceRssi'] or 0
self.connect_action_ui_update()
def init_bluesoleil(self):
try:
self.bluesoleil = BtSdk()
@ -724,6 +790,20 @@ class MainWindow(QMainWindow):
if (not _c) == 0:
QMessageBox.information(self, '关于', "" 'version: %s, build: %s, author: %s, email: %s, site: %s' % (_c[0], _c[1], _c[2], _c[3], '<a href=\'%s\'>%s</a>' % (_c[4], _c[4])))
@staticmethod
def md5(input_data):
if isinstance(input_data, bytes):
return hashlib.md5(input_data).hexdigest()
if isinstance(input_data, str):
return hashlib.md5(bytes(input_data, encoding='utf-8')).hexdigest()
md5_object = hashlib.md5()
while True:
data = input_data.read(io.DEFAULT_BUFFER_SIZE)
if data:
md5_object.update(data)
else:
return md5_object.hexdigest()
@staticmethod
def path_expandvars(path):
resolve = os.path.expandvars(path)
@ -770,6 +850,8 @@ class MainWindow(QMainWindow):
def in_connect_process(self):
self.edtBtName.setStyleSheet('font-size: 16px; font-family: \'Microsoft YaHei\'; color: #303030; border: 1px solid #808080; font-weight: bold; background-color: #7BD136')
self.edtBtName.setText('')
self.edtBtRssi.setStyleSheet('font-size: 16px; font-family: \'Microsoft YaHei\'; color: #303030; border: 1px solid #808080; font-weight: bold; background-color: #7BD136')
self.edtBtRssi.setText('')
self.edtBtAddr.setText('')
self.edtBtAddr.setEnabled(False)
self.edtBtAddr.setFocus()
@ -779,6 +861,8 @@ class MainWindow(QMainWindow):
def un_connect_process(self):
self.edtBtName.setStyleSheet('font-size: 16px; font-family: \'Microsoft YaHei\'; color: #303030; border: 1px solid #808080; font-weight: bold; background-color: #FDD391')
self.edtBtName.setText('')
self.edtBtRssi.setStyleSheet('font-size: 16px; font-family: \'Microsoft YaHei\'; color: #303030; border: 1px solid #808080; font-weight: bold; background-color: #F5F5F5')
self.edtBtRssi.setText('')
self.edtBtAddr.setText('')
self.edtBtAddr.setEnabled(True)
self.edtBtAddr.setFocus()
@ -786,8 +870,13 @@ class MainWindow(QMainWindow):
self.butManuSearch.setEnabled(True)
def on_disconnect(self):
self.connect_status_update_timer.stop()
self.bluesoleil.cancelDeviceDiscovery()
self.bluesoleil.removeAllDevices()
self.currentDevHandle = 0
self.currentDevRssi = 0
self.setting['deviceHandle'] = 0
self.setting['deviceRssi'] = 0
self.un_connect_process()
def on_autosearch(self):
@ -826,22 +915,41 @@ class MainWindow(QMainWindow):
return None
return self.connect_action_ui(text)
def connect_action_ui_update(self):
self.connect_status_update_timer.start(1500)
self.edtBtAddr.setText(str(self.bluesoleil.getDeviceAddr(self.currentDevHandle)).replace(':', ''))
self.edtBtName.setText(str(self.bluesoleil.getDeviceName(self.currentDevHandle)))
self.edtBtRssi.setText(str(self.currentDevRssi if self.currentDevRssi != -3 else ''))
if -3 == self.currentDevRssi:
self.edtBtRssi.setStyleSheet('font-size: 16px; font-family: \'Microsoft YaHei\'; color: #303030; border: 1px solid #808080; font-weight: bold; background-color: #FFF3F3')
if -65 < self.currentDevRssi <= -5:
self.edtBtRssi.setStyleSheet('font-size: 16px; font-family: \'Microsoft YaHei\'; color: #303030; border: 1px solid #808080; font-weight: bold; background-color: #73CD2A')
if -72 < self.currentDevRssi <= -65:
self.edtBtRssi.setStyleSheet('font-size: 16px; font-family: \'Microsoft YaHei\'; color: #303030; border: 1px solid #808080; font-weight: bold; background-color: #F2B32D')
if -99 < self.currentDevRssi <= -72:
self.edtBtRssi.setStyleSheet('font-size: 16px; font-family: \'Microsoft YaHei\'; color: #303030; border: 1px solid #808080; font-weight: bold; background-color: #F25555')
def connect_action_ui(self, handle):
if (handle != 0) == 1:
self.in_connect_process()
self.setWindowTitle(self.app_text['connecting'])
if (not self.connect(handle)) == 1:
self.currentDevRssi = self.connect(handle) or 0
else:
self.currentDevRssi = 0
if (not self.currentDevRssi) == 1:
self.un_connect_process()
self.edtBtAddr.selectAll()
self.setWindowTitle(self.app_name)
QMessageBox.critical(self, '提示', '连接失败')
return None
else:
self.edtBtAddr.setText(str(self.bluesoleil.getDeviceAddr(self.currentDevHandle)).replace(':', ''))
self.edtBtName.setText(str(self.bluesoleil.getDeviceName(self.currentDevHandle)))
self.connect_action_ui_update()
self.setWindowTitle(self.app_name)
return True
def connect_status_update(self):
self.bluesoleil.isBluetoothActive() or self.on_disconnect()
def connect(self, data):
devHandle = data
if (isinstance(data, str)) == 1:
@ -853,11 +961,23 @@ class MainWindow(QMainWindow):
break
if (not devHandle) == 1:
return None
rssi = 0
for i in range(8):
time.sleep(0.25)
rssi = self.bluesoleil.getDeviceRSSI(devHandle)
if (rssi != 0) == 1:
break
code = self.bluesoleil.connectAudioService(devHandle)
if (not code) == 1:
return None
self.currentDevHandle = devHandle
return True
self.setting['deviceHandle'] = devHandle
self.setting['deviceRssi'] = rssi
if (not rssi and code > 0) == 1:
return -3
else:
return rssi
class ManuWindow(QDialog):
def __init__(self, mainwindow):
@ -938,10 +1058,11 @@ class ManuWindow(QDialog):
self.mainwindow.bluesoleil.enableDeviceDiscovery()
except AttributeError:
self.close()
self.mainwindow.toast.show_toast('正在搜索设备')
timer = QTimer(self)
timer.timeout.connect(self.task_end_search)
timer.setSingleShot(True)
timer.start(7000)
timer.start(9999)
def on_connect(self, data):
self.mainwindow.bluesoleil.cancelDeviceDiscovery()
@ -956,6 +1077,46 @@ class ManuWindow(QDialog):
pass
class ToastNotification(QDialog):
def __init__(self):
super().__init__()
self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.WindowType.Tool)
layout = QVBoxLayout()
layout.setContentsMargins(5, 705, 5, 5)
self.label = QLabel("")
self.label.setStyleSheet("font-family: 'Microsoft YaHei'; font-size: 24px; color: #ffffff;")
layout.addWidget(self.label)
self.setLayout(layout)
self.setAttribute(Qt.WA_TranslucentBackground)
self.setStyleSheet("background-color: rgba(65, 65, 65, 180); border-radius: 5px; padding: 10px 16px 10px 16px")
self.adjustSize()
self.timer = QTimer(self)
self.timer.setInterval(2000)
self.timer.timeout.connect(self.close)
self.fade_animation = QPropertyAnimation(self, b"windowOpacity")
self.fade_animation.setDuration(500)
def show_toast(self, message):
self.close()
self.label.setText(message)
self.timer.isActive() and self.timer.stop()
self.timer.start()
self.adjustSize()
self.fade_animation.setStartValue(0.0)
self.fade_animation.setEndValue(1.0)
self.show()
self.fade_animation.start()
def closeEvent(self, event):
if self.timer.isActive():
self.timer.stop()
super().closeEvent(event)
if __name__ == '__main__':
if (os.path.basename(__file__).lower().endswith('.int')) == 1:
QCoreApplication.addLibraryPath(os.path.abspath(os.path.join(os.path.dirname(__file__), 'site-packages/PyQt5/Qt5/plugins')))