|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2023 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing a dialog to scan for Bluetooth devices. |
|
8 """ |
|
9 |
|
10 from PyQt6.QtCore import Qt, pyqtSlot |
|
11 from PyQt6.QtWidgets import QHeaderView, QTreeWidgetItem, QWidget |
|
12 |
|
13 from eric7.EricGui.EricOverrideCursor import EricOverrideCursor |
|
14 from eric7.EricWidgets import EricMessageBox |
|
15 |
|
16 from .Ui_BluetoothScanWindow import Ui_BluetoothScanWindow |
|
17 |
|
18 |
|
19 class BluetoothScanWindow(QWidget, Ui_BluetoothScanWindow): |
|
20 """ |
|
21 Class implementing a dialog to scan for Bluetooth devices. |
|
22 """ |
|
23 |
|
24 def __init__(self, device, parent=None): |
|
25 """ |
|
26 Constructor |
|
27 |
|
28 @param device reference to the connected device |
|
29 @type BaseDevice |
|
30 @param parent reference to the parent widget (defaults to None) |
|
31 @type QWidget (optional) |
|
32 """ |
|
33 super().__init__(parent) |
|
34 self.setupUi(self) |
|
35 |
|
36 windowFlags = self.windowFlags() |
|
37 windowFlags |= Qt.WindowType.Window |
|
38 windowFlags |= Qt.WindowType.WindowContextHelpButtonHint |
|
39 self.setWindowFlags(windowFlags) |
|
40 |
|
41 self.__device = device |
|
42 |
|
43 self.devicesList.setColumnCount(4) |
|
44 self.devicesList.headerItem().setText(3, "") |
|
45 |
|
46 self.scanButton.clicked.connect(self.scanDevices) |
|
47 |
|
48 self.devicesList.sortByColumn(0, Qt.SortOrder.AscendingOrder) |
|
49 |
|
50 @pyqtSlot() |
|
51 def scanDevices(self): |
|
52 """ |
|
53 Public slot to ask the device for a Bluetooth scan and display the result. |
|
54 """ |
|
55 self.devicesList.clear() |
|
56 self.statusLabel.clear() |
|
57 |
|
58 self.scanButton.setEnabled(False) |
|
59 with EricOverrideCursor(): |
|
60 scanResults, error = self.__device.getDeviceScan( |
|
61 timeout=self.durationSpinBox.value() |
|
62 ) |
|
63 self.scanButton.setEnabled(True) |
|
64 |
|
65 if error: |
|
66 EricMessageBox.warning( |
|
67 self, |
|
68 self.tr("Bluetooth Scan"), |
|
69 self.tr( |
|
70 """<p>The scan for available devices failed.</p>""" |
|
71 """<p>Reason: {0}</p>""" |
|
72 ).format(error), |
|
73 ) |
|
74 |
|
75 else: |
|
76 for res in scanResults.values(): |
|
77 name = res.name |
|
78 if not name: |
|
79 name = self.tr("N/A") |
|
80 itm = QTreeWidgetItem( |
|
81 self.devicesList, [name, res.address, str(res.rssi)] |
|
82 ) |
|
83 itm.setTextAlignment(1, Qt.AlignmentFlag.AlignHCenter) |
|
84 itm.setTextAlignment(2, Qt.AlignmentFlag.AlignHCenter) |
|
85 |
|
86 for service, isComplete in res.services: |
|
87 if len(service) == 6: |
|
88 bits = 16 |
|
89 elif len(service) == 10: |
|
90 bits = 32 |
|
91 else: |
|
92 bits = 128 |
|
93 template = ( |
|
94 self.tr("Complete {0}-bit Service UUID: {1}") |
|
95 if isComplete |
|
96 else self.tr("Incomplete {0}-bit Service UUID: {1}") |
|
97 ) |
|
98 sitm = QTreeWidgetItem(itm, [template.format(bits, service)]) |
|
99 sitm.setFirstColumnSpanned(True) |
|
100 |
|
101 for mid, _, mname in res.manufacturer(withName=True): |
|
102 mitm = QTreeWidgetItem( |
|
103 itm, |
|
104 [ |
|
105 self.tr("Manufacturer ID: 0x{0:x} ({1})").format(mid, mname) |
|
106 if bool(mname) |
|
107 else self.tr("Manufacturer ID: 0x{0:x}").format(mid) |
|
108 ], |
|
109 ) |
|
110 mitm.setFirstColumnSpanned(True) |
|
111 |
|
112 self.__resizeColumns() |
|
113 self.__resort() |
|
114 |
|
115 def __resort(self): |
|
116 """ |
|
117 Private method to resort the devices list. |
|
118 """ |
|
119 self.devicesList.sortItems( |
|
120 self.devicesList.sortColumn(), |
|
121 self.devicesList.header().sortIndicatorOrder(), |
|
122 ) |
|
123 |
|
124 def __resizeColumns(self): |
|
125 """ |
|
126 Private method to resize the columns of the result list. |
|
127 """ |
|
128 self.devicesList.header().resizeSections( |
|
129 QHeaderView.ResizeMode.ResizeToContents |
|
130 ) |
|
131 self.devicesList.header().setStretchLastSection(True) |