bl new design
This commit is contained in:
parent
7f577613d3
commit
1e2bfe9380
@ -1,6 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from PySide6.QtCore import Qt, QTimer, QSettings, QEvent
|
||||
from PySide6.QtCore import Qt, QTimer, QSettings, QEvent, Signal
|
||||
from PySide6.QtGui import QFont
|
||||
from PySide6.QtWidgets import (
|
||||
QWidget,
|
||||
@ -11,12 +11,102 @@ from PySide6.QtWidgets import (
|
||||
QListWidget,
|
||||
QListWidgetItem,
|
||||
QScroller,
|
||||
QFrame,
|
||||
)
|
||||
|
||||
import build_info
|
||||
from services.bluetooth_service import BluetoothService, BluetoothDevice
|
||||
|
||||
|
||||
class BluetoothDeviceCard(QFrame):
|
||||
"""Карточка Bluetooth устройства."""
|
||||
|
||||
connect_clicked = Signal(str) # mac
|
||||
disconnect_clicked = Signal(str) # mac
|
||||
remove_clicked = Signal(str) # mac
|
||||
|
||||
def __init__(self, device: BluetoothDevice, connected: bool, parent=None):
|
||||
super().__init__(parent)
|
||||
self._device = device
|
||||
self._connected = connected
|
||||
|
||||
self.setObjectName("BluetoothDeviceCard")
|
||||
self.setFrameShape(QFrame.StyledPanel)
|
||||
self.setCursor(Qt.PointingHandCursor)
|
||||
|
||||
root = QHBoxLayout(self)
|
||||
root.setContentsMargins(14, 10, 14, 10)
|
||||
root.setSpacing(12)
|
||||
|
||||
# Индикатор статуса (цветная точка)
|
||||
self.status_indicator = QLabel("●")
|
||||
self.status_indicator.setObjectName("BluetoothStatusIndicator")
|
||||
self.status_indicator.setFont(QFont("", 18))
|
||||
self.status_indicator.setAlignment(Qt.AlignCenter)
|
||||
self.status_indicator.setFixedSize(24, 24)
|
||||
self._update_status_indicator()
|
||||
|
||||
# Информация об устройстве
|
||||
info_col = QVBoxLayout()
|
||||
info_col.setContentsMargins(0, 0, 0, 0)
|
||||
info_col.setSpacing(2)
|
||||
|
||||
self.name_label = QLabel(device.name if device.name else "Неизвестное устройство")
|
||||
self.name_label.setObjectName("BluetoothDeviceName")
|
||||
self.name_label.setFont(QFont("", 15, 600))
|
||||
|
||||
self.mac_label = QLabel(device.mac)
|
||||
self.mac_label.setObjectName("BluetoothDeviceMac")
|
||||
self.mac_label.setFont(QFont("", 11))
|
||||
|
||||
self.status_label = QLabel("Подключено" if connected else "Не подключено")
|
||||
self.status_label.setObjectName("BluetoothDeviceStatus")
|
||||
self.status_label.setFont(QFont("", 12))
|
||||
|
||||
info_col.addWidget(self.name_label)
|
||||
info_col.addWidget(self.mac_label)
|
||||
info_col.addWidget(self.status_label)
|
||||
|
||||
# Кнопка удаления (крестик)
|
||||
self.remove_btn = QPushButton("✕")
|
||||
self.remove_btn.setObjectName("BluetoothRemoveBtn")
|
||||
self.remove_btn.setFixedSize(36, 36)
|
||||
self.remove_btn.setCursor(Qt.PointingHandCursor)
|
||||
self.remove_btn.clicked.connect(self._on_remove)
|
||||
|
||||
root.addWidget(self.status_indicator)
|
||||
root.addLayout(info_col, 1)
|
||||
root.addWidget(self.remove_btn)
|
||||
|
||||
def _update_status_indicator(self):
|
||||
"""Обновить цвет индикатора."""
|
||||
if self._connected:
|
||||
self.status_indicator.setStyleSheet("color: #4ade80;") # зелёный
|
||||
self.status_label.setStyleSheet("color: #4ade80;")
|
||||
self.status_label.setText("Подключено")
|
||||
else:
|
||||
self.status_indicator.setStyleSheet("color: #9ca3af;") # серый
|
||||
self.status_label.setStyleSheet("color: #9ca3af;")
|
||||
self.status_label.setText("Не подключено")
|
||||
|
||||
def update_connected(self, connected: bool):
|
||||
"""Обновить статус подключения."""
|
||||
self._connected = connected
|
||||
self._update_status_indicator()
|
||||
|
||||
def _on_remove(self):
|
||||
"""Обработчик нажатия кнопки удаления."""
|
||||
self.remove_clicked.emit(self._device.mac)
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
"""Обработка клика по карточке."""
|
||||
if self._connected:
|
||||
self.disconnect_clicked.emit(self._device.mac)
|
||||
else:
|
||||
self.connect_clicked.emit(self._device.mac)
|
||||
super().mousePressEvent(event)
|
||||
|
||||
|
||||
class BluetoothScreen(QWidget):
|
||||
def __init__(self, on_back):
|
||||
super().__init__()
|
||||
@ -38,9 +128,8 @@ class BluetoothScreen(QWidget):
|
||||
|
||||
self.list = QListWidget()
|
||||
self.list.setObjectName("BluetoothList")
|
||||
self.list.setSpacing(6)
|
||||
self.list.setSelectionMode(QListWidget.SingleSelection)
|
||||
self.list.itemSelectionChanged.connect(self._on_select)
|
||||
self.list.setSpacing(8)
|
||||
self.list.setSelectionMode(QListWidget.NoSelection) # убираем выделение, т.к. карточки кликабельны
|
||||
|
||||
QScroller.scroller(self.list.viewport()).grabGesture(
|
||||
self.list.viewport(),
|
||||
@ -67,14 +156,12 @@ class BluetoothScreen(QWidget):
|
||||
self.btn_connect = QPushButton("Подключить")
|
||||
self.btn_connect.setObjectName("BluetoothActionBtnPrimary")
|
||||
self.btn_connect.setMinimumHeight(56)
|
||||
self.btn_connect.setEnabled(False)
|
||||
self.btn_connect.clicked.connect(self._connect_selected)
|
||||
self.btn_connect.clicked.connect(self._connect_last_selected)
|
||||
|
||||
self.btn_disconnect = QPushButton("Отключить")
|
||||
self.btn_disconnect.setObjectName("BluetoothActionBtn")
|
||||
self.btn_disconnect.setMinimumHeight(56)
|
||||
self.btn_disconnect.setEnabled(False)
|
||||
self.btn_disconnect.clicked.connect(self._disconnect_selected)
|
||||
self.btn_disconnect.clicked.connect(self._disconnect_last_selected)
|
||||
|
||||
actions.addWidget(self.btn_visible, 1)
|
||||
actions.addWidget(self.btn_refresh, 1)
|
||||
@ -92,35 +179,73 @@ class BluetoothScreen(QWidget):
|
||||
"""Обновить список сопряженных устройств."""
|
||||
devices = self._bt_service.get_paired_devices()
|
||||
self.list.clear()
|
||||
self._cards: dict[str, BluetoothDeviceCard] = {}
|
||||
|
||||
for dev in devices:
|
||||
label = f"{dev.name} ({dev.mac})" if dev.name else dev.mac
|
||||
item = QListWidgetItem(label)
|
||||
item.setData(Qt.UserRole, dev.mac)
|
||||
connected = self._bt_service.is_connected(dev.mac)
|
||||
card = BluetoothDeviceCard(dev, connected, self)
|
||||
card.connect_clicked.connect(self._connect_device)
|
||||
card.disconnect_clicked.connect(self._disconnect_device)
|
||||
card.remove_clicked.connect(self._remove_device)
|
||||
|
||||
item = QListWidgetItem()
|
||||
item.setSizeHint(card.sizeHint())
|
||||
self.list.addItem(item)
|
||||
self.list.setItemWidget(item, card)
|
||||
self._cards[dev.mac] = card
|
||||
|
||||
self._update_status()
|
||||
|
||||
def _on_select(self):
|
||||
"""Обработчик выбора устройства."""
|
||||
has_selection = self._selected_mac() is not None
|
||||
self.btn_connect.setEnabled(has_selection)
|
||||
self.btn_disconnect.setEnabled(has_selection)
|
||||
self._update_status()
|
||||
def _connect_last_selected(self):
|
||||
"""Подключить последнее выбранное устройство."""
|
||||
if self._cards:
|
||||
last_mac = self._settings.value("bluetooth/last_mac", "")
|
||||
if last_mac and last_mac in self._cards:
|
||||
self._connect_device(last_mac)
|
||||
|
||||
def _selected_mac(self) -> str | None:
|
||||
"""Получить MAC-адрес выбранного устройства."""
|
||||
items = self.list.selectedItems()
|
||||
if not items:
|
||||
return None
|
||||
return items[0].data(Qt.UserRole)
|
||||
def _disconnect_last_selected(self):
|
||||
"""Отключить последнее выбранное устройство."""
|
||||
if self._cards:
|
||||
last_mac = self._settings.value("bluetooth/last_mac", "")
|
||||
if last_mac and last_mac in self._cards:
|
||||
self._disconnect_device(last_mac)
|
||||
|
||||
def _auto_connect_last(self):
|
||||
"""Автоподключение к последнему устройству."""
|
||||
last_mac = self._settings.value("bluetooth/last_mac", "")
|
||||
if not last_mac:
|
||||
def _connect_device(self, mac: str):
|
||||
"""Подключиться к устройству по MAC."""
|
||||
if mac not in self._cards:
|
||||
return
|
||||
self._connect_device(last_mac, silent=True)
|
||||
success = self._bt_service.connect_device(mac)
|
||||
if not success:
|
||||
self.status.setText(f"Статус: ошибка ({self._bt_service.last_error})")
|
||||
self._settings.setValue("bluetooth/last_mac", mac)
|
||||
QTimer.singleShot(300, self._update_card_status, mac)
|
||||
|
||||
def _disconnect_device(self, mac: str):
|
||||
"""Отключить устройство по MAC."""
|
||||
if mac not in self._cards:
|
||||
return
|
||||
success = self._bt_service.disconnect_device(mac)
|
||||
if not success:
|
||||
self.status.setText(f"Статус: ошибка ({self._bt_service.last_error})")
|
||||
else:
|
||||
self.status.setText(f"Статус: отключено от {mac}")
|
||||
QTimer.singleShot(300, self._update_card_status, mac)
|
||||
|
||||
def _remove_device(self, mac: str):
|
||||
"""Удалить устройство из списка сопряженных."""
|
||||
success = self._bt_service.remove_device(mac)
|
||||
if success:
|
||||
self.status.setText(f"Статус: устройство {mac} удалено")
|
||||
QTimer.singleShot(300, self.refresh_list)
|
||||
else:
|
||||
self.status.setText(f"Статус: ошибка удаления ({self._bt_service.last_error})")
|
||||
|
||||
def _update_card_status(self, mac: str):
|
||||
"""Обновить статус на карточке устройства."""
|
||||
if mac in self._cards:
|
||||
connected = self._bt_service.is_connected(mac)
|
||||
self._cards[mac].update_connected(connected)
|
||||
self._update_status()
|
||||
|
||||
def _make_visible(self):
|
||||
"""Сделать устройство видимым для сопряжения."""
|
||||
@ -142,40 +267,13 @@ class BluetoothScreen(QWidget):
|
||||
self._bt_service.make_discoverable()
|
||||
self._discoverable_timer.start(9000) # 9 секунд
|
||||
|
||||
def _connect_selected(self):
|
||||
"""Подключить выбранное устройство."""
|
||||
mac = self._selected_mac()
|
||||
if not mac:
|
||||
return
|
||||
self._connect_device(mac, silent=False)
|
||||
|
||||
def _connect_device(self, mac: str, silent: bool):
|
||||
"""Подключиться к устройству по MAC."""
|
||||
success = self._bt_service.connect_device(mac)
|
||||
if not silent:
|
||||
if not success:
|
||||
self.status.setText(f"Статус: ошибка ({self._bt_service.last_error})")
|
||||
else:
|
||||
self.status.setText(f"Статус: подключаемся к {mac}")
|
||||
self._settings.setValue("bluetooth/last_mac", mac)
|
||||
QTimer.singleShot(300, self._update_status)
|
||||
|
||||
def _disconnect_selected(self):
|
||||
"""Отключить выбранное устройство."""
|
||||
mac = self._selected_mac()
|
||||
if not mac:
|
||||
return
|
||||
success = self._bt_service.disconnect_device(mac)
|
||||
if not success:
|
||||
self.status.setText(f"Статус: ошибка ({self._bt_service.last_error})")
|
||||
else:
|
||||
self.status.setText(f"Статус: отключено от {mac}")
|
||||
QTimer.singleShot(300, self._update_status)
|
||||
|
||||
def _update_status(self):
|
||||
"""Обновить текстовый статус."""
|
||||
mac = self._selected_mac()
|
||||
self.status.setText(self._bt_service.get_status_text(mac))
|
||||
last_mac = self._settings.value("bluetooth/last_mac", "")
|
||||
if not last_mac or last_mac not in self._cards:
|
||||
self.status.setText("Статус: —")
|
||||
return
|
||||
self.status.setText(self._bt_service.get_status_text(last_mac))
|
||||
|
||||
def showEvent(self, event):
|
||||
"""Экран показан — запускаем режим сопряжения и обновление списка."""
|
||||
|
||||
@ -81,6 +81,16 @@ class BluetoothService(QObject):
|
||||
self.connected_changed.emit(mac, False)
|
||||
return success
|
||||
|
||||
def remove_device(self, mac: str) -> bool:
|
||||
"""Удалить устройство из списка сопряженных."""
|
||||
self._last_error = ""
|
||||
# Сначала отключаем, если подключено
|
||||
if self.is_connected(mac):
|
||||
self._run_cmd(["bluetoothctl", "disconnect", mac])
|
||||
result = self._run_cmd(["bluetoothctl", "remove", mac])
|
||||
success = not self._last_error
|
||||
return success
|
||||
|
||||
def make_discoverable(self, timeout_sec: int = 10) -> bool:
|
||||
"""Сделать устройство видимым для сопряжения.
|
||||
|
||||
|
||||
@ -140,10 +140,31 @@ QScrollArea > QWidget > QWidget { background: transparent; }
|
||||
padding: 6px;
|
||||
}
|
||||
#BluetoothList::item {
|
||||
padding: 12px 10px;
|
||||
padding: 0;
|
||||
border-radius: 10px;
|
||||
}
|
||||
#BluetoothList::item:selected { background: #F3F4F6; }
|
||||
#BluetoothDeviceCard {
|
||||
background: #FFFFFF;
|
||||
border-radius: 14px;
|
||||
border: 1px solid #E5E7EB;
|
||||
}
|
||||
#BluetoothDeviceCard:hover { background: #F9FAFB; }
|
||||
#BluetoothDeviceName { color: #111827; }
|
||||
#BluetoothDeviceMac { color: rgba(107,114,128,0.95); }
|
||||
#BluetoothDeviceStatus { color: rgba(107,114,128,0.95); }
|
||||
#BluetoothStatusIndicator { background: transparent; }
|
||||
#BluetoothRemoveBtn {
|
||||
background: transparent;
|
||||
color: rgba(107,114,128,0.95);
|
||||
border-radius: 18px;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
}
|
||||
#BluetoothRemoveBtn:hover {
|
||||
background: #FEE2E2;
|
||||
color: #DC2626;
|
||||
}
|
||||
#BluetoothActionBtn {
|
||||
background: #FFFFFF;
|
||||
border-radius: 12px;
|
||||
|
||||
@ -130,10 +130,30 @@ QScrollArea > QWidget > QWidget { background: transparent; }
|
||||
padding: 6px;
|
||||
}
|
||||
#BluetoothList::item {
|
||||
padding: 12px 10px;
|
||||
padding: 0;
|
||||
border-radius: 10px;
|
||||
}
|
||||
#BluetoothList::item:selected { background: #1B2330; }
|
||||
#BluetoothDeviceCard {
|
||||
background: #141A22;
|
||||
border-radius: 14px;
|
||||
}
|
||||
#BluetoothDeviceCard:hover { background: #1B2330; }
|
||||
#BluetoothDeviceName { color: #E6EAF0; }
|
||||
#BluetoothDeviceMac { color: rgba(138,147,166,0.95); }
|
||||
#BluetoothDeviceStatus { color: rgba(138,147,166,0.95); }
|
||||
#BluetoothStatusIndicator { background: transparent; }
|
||||
#BluetoothRemoveBtn {
|
||||
background: transparent;
|
||||
color: rgba(138,147,166,0.95);
|
||||
border-radius: 18px;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
}
|
||||
#BluetoothRemoveBtn:hover {
|
||||
background: #B91C1C;
|
||||
color: #FEE2E2;
|
||||
}
|
||||
#BluetoothActionBtn {
|
||||
background: #141A22;
|
||||
border-radius: 12px;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user