multiserialviewer 24.12.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. multiserialviewer/__init__.py +1 -0
  2. multiserialviewer/__main__.py +12 -0
  3. multiserialviewer/__version__.py +13 -0
  4. multiserialviewer/application/__init__.py +0 -0
  5. multiserialviewer/application/application.py +225 -0
  6. multiserialviewer/application/proxyStyle.py +10 -0
  7. multiserialviewer/application/serialViewerController.py +50 -0
  8. multiserialviewer/gui/__init__.py +0 -0
  9. multiserialviewer/gui/mainWindow.py +80 -0
  10. multiserialviewer/gui/serialViewerCreateDialog.py +96 -0
  11. multiserialviewer/gui/serialViewerWindow.py +67 -0
  12. multiserialviewer/gui/textHighlighterSettingsDialog.py +46 -0
  13. multiserialviewer/serial_data/__init__.py +0 -0
  14. multiserialviewer/serial_data/serialConnectionSettings.py +11 -0
  15. multiserialviewer/serial_data/serialDataProcessor.py +51 -0
  16. multiserialviewer/serial_data/serialDataReceiver.py +63 -0
  17. multiserialviewer/text_highlighter/__init__.py +0 -0
  18. multiserialviewer/text_highlighter/colorSelectorItemDelegate.py +41 -0
  19. multiserialviewer/text_highlighter/textHighlighter.py +32 -0
  20. multiserialviewer/text_highlighter/textHighlighterConfig.py +19 -0
  21. multiserialviewer/text_highlighter/textHighlighterTableModel.py +120 -0
  22. multiserialviewer/ui_files/__init__.py +0 -0
  23. multiserialviewer/ui_files/createSerialViewerDialog.ui +209 -0
  24. multiserialviewer/ui_files/mainWindow.ui +82 -0
  25. multiserialviewer/ui_files/serialViewerWindow.ui +72 -0
  26. multiserialviewer/ui_files/textHighlighterSettingsDialog.ui +93 -0
  27. multiserialviewer/ui_files/uiFileHelper.py +17 -0
  28. multiserialviewer-24.12.0.dist-info/LICENSE +674 -0
  29. multiserialviewer-24.12.0.dist-info/METADATA +23 -0
  30. multiserialviewer-24.12.0.dist-info/RECORD +33 -0
  31. multiserialviewer-24.12.0.dist-info/WHEEL +5 -0
  32. multiserialviewer-24.12.0.dist-info/entry_points.txt +2 -0
  33. multiserialviewer-24.12.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1 @@
1
+ from .__version__ import __version__
@@ -0,0 +1,12 @@
1
+ import sys
2
+ from multiserialviewer import __version__
3
+ from multiserialviewer.application.application import Application
4
+
5
+
6
+ def main() -> int | str:
7
+ app = Application(__version__, sys.argv)
8
+ return app.exec()
9
+
10
+
11
+ if __name__ == '__main__':
12
+ sys.exit(main())
@@ -0,0 +1,13 @@
1
+ # Version scheme:
2
+ # YEAR.MONTH.COUNTER
3
+ # YEAR, MONTH are two digits and zero-padded
4
+ # COUNTER resets every month and starts with 0
5
+ # For example:
6
+ # - 23.09.0
7
+ # - 23.09.1
8
+ # - 23.10.0
9
+ #
10
+ # Development versions contain the DEV-COUNTER postfix:
11
+ # - 24.01.0.DEV-1
12
+
13
+ __version__ = '24.12.0'
File without changes
@@ -0,0 +1,225 @@
1
+ from PySide6.QtWidgets import QApplication
2
+ from PySide6.QtCore import QSettings, QSize, Slot
3
+ from typing import List
4
+ from platformdirs import user_config_dir
5
+ import pathlib
6
+ import copy
7
+
8
+ from multiserialviewer.gui.mainWindow import MainWindow
9
+ from multiserialviewer.serial_data.serialDataReceiver import SerialDataReceiver
10
+ from multiserialviewer.serial_data.serialDataProcessor import SerialDataProcessor
11
+ from multiserialviewer.serial_data.serialConnectionSettings import SerialConnectionSettings
12
+ from multiserialviewer.text_highlighter.textHighlighterConfig import TextHighlighterConfig
13
+ from multiserialviewer.application.serialViewerController import SerialViewerController
14
+ from multiserialviewer.application.proxyStyle import ProxyStyle
15
+
16
+
17
+
18
+
19
+ class Application(QApplication):
20
+ NAME = 'MultiSerialViewer'
21
+
22
+ def __init__(self, version: str, arguments):
23
+ super().__init__(arguments)
24
+
25
+ self.config_dir = user_config_dir(appname=Application.NAME, roaming=False, ensure_exists=True, appauthor=False)
26
+ self.main_config_file_path = str(pathlib.PurePath(self.config_dir, 'multiserialviewer.ini'))
27
+ self.highlighter_config_file_path = str(pathlib.PurePath(self.config_dir, 'highlighter.ini'))
28
+
29
+ self.controller = {}
30
+ self.highlighterSettings: List[TextHighlighterConfig] = []
31
+
32
+ self.mainWindow = MainWindow(f'{Application.NAME} {version}')
33
+ self.mainWindow.signal_showSerialViewerCreateDialog.connect(self.showSerialViewerCreateDialog)
34
+ self.mainWindow.signal_createSerialViewer.connect(self.createSerialViewer)
35
+ self.mainWindow.signal_clearAll.connect(self.clearAll)
36
+ self.mainWindow.signal_connectionStateChanged.connect(self.changeConnectionState)
37
+ self.mainWindow.signal_aboutToBeClosed.connect(self.saveSettings)
38
+ self.mainWindow.signal_aboutToBeClosed.connect(self.saveSerialViewerSettings)
39
+ self.mainWindow.signal_aboutToBeClosed.connect(self.saveHighlighterSettings)
40
+ self.mainWindow.signal_aboutToBeClosed.connect(self.stopAllSerialViewer)
41
+ self.mainWindow.signal_editHighlighterSettings.connect(self.showHighlighterSettingsDialog)
42
+ self.mainWindow.signal_applyHighlighterSettings.connect(self.setHighlighterSettings)
43
+
44
+ self.loadSettings()
45
+ self.loadHighlighterSettings()
46
+ self.loadSerialViewerSettings()
47
+
48
+ self.setStyle(ProxyStyle())
49
+ self.mainWindow.show()
50
+
51
+ def initDefaultHighlighterSettings(self):
52
+ self.highlighterSettings = []
53
+
54
+ cfg = TextHighlighterConfig()
55
+ cfg.pattern = r'\[MSG: .* :MSG\]'
56
+ cfg.color_foreground = 'darkgreen'
57
+ cfg.color_background = 'transparent'
58
+ cfg.italic = False
59
+ cfg.bold = True
60
+ cfg.font_size = QApplication.font().pointSize()
61
+ self.highlighterSettings.append(cfg)
62
+
63
+ cfg = TextHighlighterConfig()
64
+ cfg.pattern = r'\[ERR: .* :ERR\]'
65
+ cfg.color_foreground = 'darkred'
66
+ cfg.color_background = 'transparent'
67
+ cfg.italic = False
68
+ cfg.bold = True
69
+ cfg.font_size = QApplication.font().pointSize()
70
+ self.highlighterSettings.append(cfg)
71
+
72
+ @Slot(object)
73
+ def setHighlighterSettings(self, settings: List[TextHighlighterConfig]):
74
+ self.highlighterSettings = settings
75
+ for ctrl in self.controller.values():
76
+ ctrl.view.setHighlighterSettings(self.highlighterSettings)
77
+
78
+ @Slot()
79
+ def showSerialViewerCreateDialog(self):
80
+ already_used_ports = list(self.controller.keys())
81
+ self.mainWindow.showSerialViewerCreateDialog(already_used_ports)
82
+
83
+ @Slot(str, SerialConnectionSettings)
84
+ def createSerialViewer(self, window_title: str, settings: SerialConnectionSettings, size: QSize = None):
85
+ if settings.portName in self.controller:
86
+ raise Exception(f"{settings.portName} exists already")
87
+
88
+ receiver = SerialDataReceiver(settings)
89
+ processor = SerialDataProcessor(receiver.rxQueue)
90
+ view = self.mainWindow.createSerialViewerWindow(window_title, size)
91
+ view.setHighlighterSettings(self.highlighterSettings)
92
+ ctrl = SerialViewerController(receiver, processor, view)
93
+
94
+ ctrl.terminated.connect(self.deleteSerialViewer)
95
+ self.controller[settings.portName] = ctrl
96
+
97
+ if self.mainWindow.getConnectionState():
98
+ ctrl.start()
99
+
100
+ @Slot()
101
+ def deleteSerialViewer(self, portName):
102
+ if portName in self.controller:
103
+ del self.controller[portName]
104
+ else:
105
+ raise Exception("Controller to remove does not exist in list")
106
+
107
+ @Slot()
108
+ def clearAll(self):
109
+ for ctrl in self.controller.values():
110
+ ctrl.view.clear()
111
+
112
+ @Slot(bool)
113
+ def changeConnectionState(self, state):
114
+ if len(self.controller.values()) > 0:
115
+ if state:
116
+ failed_to_connect = False
117
+
118
+ # try to connect all ports
119
+ for ctrl in self.controller.values():
120
+ if not ctrl.start():
121
+ failed_to_connect = True
122
+
123
+ if failed_to_connect:
124
+ # cleanup if connect failed
125
+ for ctrl in self.controller.values():
126
+ ctrl.stop()
127
+ self.mainWindow.setConnectionState(False)
128
+ else:
129
+ self.mainWindow.setConnectionState(True)
130
+ else:
131
+ for ctrl in self.controller.values():
132
+ ctrl.stop()
133
+ self.mainWindow.setConnectionState(False)
134
+
135
+ def loadSettings(self):
136
+ settings = QSettings(self.main_config_file_path, QSettings.Format.IniFormat)
137
+
138
+ settings.beginGroup("MainWindow")
139
+ self.mainWindow.resize(settings.value("size", QSize(800, 800)))
140
+ settings.endGroup()
141
+
142
+ def saveSettings(self):
143
+ settings = QSettings(self.main_config_file_path, QSettings.Format.IniFormat)
144
+
145
+ settings.beginGroup("MainWindow")
146
+ settings.setValue("size", self.mainWindow.size())
147
+ settings.endGroup()
148
+
149
+ def loadSerialViewerSettings(self):
150
+ settings = QSettings(self.main_config_file_path, QSettings.Format.IniFormat)
151
+
152
+ number_of_connections = settings.beginReadArray("connections")
153
+ for i in range(number_of_connections):
154
+ settings.setArrayIndex(i)
155
+
156
+ # check if all needed keys exist
157
+ if all(elem in settings.allKeys() for elem in ['serialViewer', 'view/size', 'view/title']):
158
+ self.createSerialViewer(settings.value("view/title"), settings.value("serialViewer"),
159
+ settings.value("view/size"))
160
+ settings.endArray()
161
+
162
+ @Slot()
163
+ def saveSerialViewerSettings(self):
164
+ settings = QSettings(self.main_config_file_path, QSettings.Format.IniFormat)
165
+
166
+ settings.beginWriteArray("connections")
167
+ settings.remove("") # remove all existing connections
168
+
169
+ for i, ctrl in enumerate(self.controller.values()):
170
+ settings.setArrayIndex(i)
171
+ settings.setValue("serialViewer", ctrl.receiver.settings)
172
+ settings.setValue("view/title", ctrl.view.windowTitle())
173
+ settings.setValue("view/size", ctrl.view.size())
174
+
175
+ settings.endArray()
176
+
177
+ def loadHighlighterSettings(self):
178
+ settings = QSettings(self.highlighter_config_file_path, QSettings.Format.IniFormat)
179
+
180
+ number_of_settings = settings.beginReadArray("settings")
181
+ if number_of_settings > 0:
182
+ self.highlighterSettings = []
183
+
184
+ for i in range(number_of_settings):
185
+ settings.setArrayIndex(i)
186
+
187
+ # check if all needed keys exist
188
+ if all(elem in settings.allKeys() for elem in ['pattern', 'color_foreground', 'color_background', 'italic', 'bold']):
189
+ cfg = TextHighlighterConfig()
190
+ cfg.pattern = settings.value("pattern")
191
+ cfg.color_foreground = settings.value("color_foreground")
192
+ cfg.color_background = settings.value("color_background")
193
+ cfg.italic = settings.value("italic", type=bool)
194
+ cfg.bold = settings.value("bold", type=bool)
195
+ cfg.font_size = settings.value("font_size", type=int)
196
+ self.highlighterSettings.append(cfg)
197
+ settings.endArray()
198
+ else:
199
+ self.initDefaultHighlighterSettings()
200
+
201
+ @Slot()
202
+ def saveHighlighterSettings(self):
203
+ settings = QSettings(self.highlighter_config_file_path, QSettings.Format.IniFormat)
204
+
205
+ settings.beginWriteArray("settings")
206
+ settings.remove("") # remove all existing settings
207
+
208
+ for i, cfg in enumerate(self.highlighterSettings):
209
+ settings.setArrayIndex(i)
210
+ settings.setValue("pattern", cfg.pattern)
211
+ settings.setValue("color_foreground", cfg.color_foreground)
212
+ settings.setValue("color_background", cfg.color_background)
213
+ settings.setValue("italic", cfg.italic)
214
+ settings.setValue("bold", cfg.bold)
215
+ settings.setValue("font_size", cfg.font_size)
216
+ settings.endArray()
217
+
218
+ @Slot()
219
+ def stopAllSerialViewer(self):
220
+ for ctrl in self.controller.values():
221
+ ctrl.stop()
222
+
223
+ @Slot()
224
+ def showHighlighterSettingsDialog(self):
225
+ self.mainWindow.showHighlighterSettingsDialog(copy.deepcopy(self.highlighterSettings))
@@ -0,0 +1,10 @@
1
+ from PySide6.QtWidgets import QProxyStyle, QStyle
2
+
3
+
4
+ class ProxyStyle(QProxyStyle):
5
+ def subElementRect(self, element, opt, widget=None):
6
+ if element == QStyle.SE_ItemViewItemCheckIndicator and not opt.text:
7
+ rect = super().subElementRect(element, opt, widget)
8
+ rect.moveCenter(opt.rect.center())
9
+ return rect
10
+ return super().subElementRect(element, opt, widget)
@@ -0,0 +1,50 @@
1
+ from PySide6.QtCore import QObject, Slot, Signal
2
+
3
+ from multiserialviewer.gui.serialViewerWindow import SerialViewerWindow
4
+ from multiserialviewer.serial_data.serialDataReceiver import SerialDataReceiver
5
+ from multiserialviewer.serial_data.serialDataProcessor import SerialDataProcessor
6
+
7
+
8
+ class SerialViewerController(QObject):
9
+ terminated = Signal(str)
10
+
11
+ def __init__(self, receiver: SerialDataReceiver, processor: SerialDataProcessor, view: SerialViewerWindow):
12
+ super().__init__()
13
+
14
+ self.receiver = receiver
15
+ self.processor = processor
16
+ self.view = view
17
+
18
+ self.view.closed.connect(self.terminate)
19
+ self.processor.dataAvailable.connect(self.view.appendData)
20
+
21
+ def start(self) -> bool:
22
+ if self.receiver.open_port():
23
+ self.processor.start()
24
+ self.receiver.start()
25
+ self.show_message(f'Opened {self.receiver.settings.portName}')
26
+ return True
27
+ else:
28
+ self.show_error(f'Failed to open {self.receiver.settings.portName}')
29
+ return False
30
+
31
+ def stop(self):
32
+ if self.receiver.isReceiving():
33
+ self.receiver.stop()
34
+ self.receiver.close_port()
35
+ self.processor.stop()
36
+ self.show_message(f'Closed {self.receiver.settings.portName}')
37
+
38
+ def show_message(self, text):
39
+ self.view.appendData(f'\n[MSG: {text} :MSG]\n', True)
40
+
41
+ def show_error(self, text):
42
+ self.view.appendData(f'\n[ERR: {text} :ERR]\n', True)
43
+
44
+ @Slot()
45
+ def terminate(self):
46
+ # view is already closed
47
+ self.receiver.stop()
48
+ self.receiver.close_port()
49
+ self.processor.stop()
50
+ self.terminated.emit(self.receiver.settings.portName)
File without changes
@@ -0,0 +1,80 @@
1
+ from PySide6.QtWidgets import QMdiArea, QMainWindow, QPushButton
2
+ from PySide6.QtCore import QSize, Signal
3
+
4
+ from typing import List
5
+
6
+ from multiserialviewer.ui_files.uiFileHelper import createWidgetFromUiFile
7
+ from multiserialviewer.serial_data.serialConnectionSettings import SerialConnectionSettings
8
+ from multiserialviewer.gui.serialViewerWindow import SerialViewerWindow
9
+ from multiserialviewer.gui.serialViewerCreateDialog import SerialViewerCreateDialog
10
+ from multiserialviewer.gui.textHighlighterSettingsDialog import TextHighlighterSettingsDialog
11
+ from multiserialviewer.text_highlighter.textHighlighter import TextHighlighterConfig
12
+
13
+
14
+ class MainWindow(QMainWindow):
15
+ signal_showSerialViewerCreateDialog = Signal()
16
+ signal_createSerialViewer = Signal(str, SerialConnectionSettings)
17
+ signal_clearAll = Signal()
18
+ signal_connectionStateChanged = Signal(bool)
19
+ signal_aboutToBeClosed = Signal()
20
+ signal_editHighlighterSettings = Signal()
21
+ signal_applyHighlighterSettings = Signal(object)
22
+
23
+ def __init__(self, title: str):
24
+ super(MainWindow, self).__init__()
25
+ self.setWindowTitle(title)
26
+
27
+ widget = createWidgetFromUiFile("mainWindow.ui")
28
+
29
+ self.mdiArea = widget.findChild(QMdiArea, 'mdiArea')
30
+ self.pb_changeConnectionState: QPushButton = widget.findChild(QPushButton, 'pb_changeConnectionState')
31
+
32
+ self.setCentralWidget(widget)
33
+ self.setConnectionState(False)
34
+
35
+ # connections
36
+ widget.pb_create.clicked.connect(self.signal_showSerialViewerCreateDialog)
37
+ widget.pb_clear.clicked.connect(self.signal_clearAll)
38
+ widget.pb_changeConnectionState.clicked.connect(self.signal_connectionStateChanged)
39
+ widget.pb_highlighter.clicked.connect(self.signal_editHighlighterSettings)
40
+
41
+ def showSerialViewerCreateDialog(self, disabled_ports: list):
42
+ dialog = SerialViewerCreateDialog(self)
43
+ dialog.disablePorts(disabled_ports)
44
+ if dialog.exec():
45
+ port_name = dialog.getPortName()
46
+ if len(port_name) > 0:
47
+ settings = SerialConnectionSettings(port_name)
48
+ settings.baudrate = dialog.getBaudrate()
49
+ settings.bytesize = dialog.getDataBits()
50
+ settings.parity = dialog.getParity()
51
+ settings.stopbits = dialog.getStopBits()
52
+
53
+ self.signal_createSerialViewer.emit(dialog.getName(), settings)
54
+
55
+ def createSerialViewerWindow(self, viewTitle: str, size: QSize = None):
56
+ view = SerialViewerWindow(viewTitle)
57
+ if size:
58
+ view.resize(size)
59
+ self.mdiArea.addSubWindow(view)
60
+ view.show()
61
+ return view
62
+
63
+ def showHighlighterSettingsDialog(self, settings: List[TextHighlighterConfig]):
64
+ dialog = TextHighlighterSettingsDialog(self, settings)
65
+ if dialog.exec():
66
+ self.signal_applyHighlighterSettings.emit(dialog.table_model.settings)
67
+
68
+ def getConnectionState(self):
69
+ return self.pb_changeConnectionState.isChecked()
70
+
71
+ def setConnectionState(self, state):
72
+ self.pb_changeConnectionState.setChecked(state)
73
+ if state:
74
+ self.pb_changeConnectionState.setText('Stop')
75
+ else:
76
+ self.pb_changeConnectionState.setText('Start')
77
+
78
+ def closeEvent(self, event):
79
+ self.signal_aboutToBeClosed.emit()
80
+ event.accept()
@@ -0,0 +1,96 @@
1
+ from PySide6.QtWidgets import QDialog, QVBoxLayout, QDialogButtonBox
2
+ from PySide6.QtCore import Slot
3
+ import serial.tools.list_ports
4
+ import serial
5
+
6
+ from multiserialviewer.ui_files.uiFileHelper import createWidgetFromUiFile
7
+
8
+
9
+ class SerialViewerCreateDialog(QDialog):
10
+ def __init__(self, parent):
11
+ super().__init__(parent)
12
+
13
+ self.setWindowTitle("Create SerialViewer")
14
+ self.disabled_port_names = []
15
+
16
+ self.connectWidget = createWidgetFromUiFile("createSerialViewerDialog.ui")
17
+
18
+ self.refreshListOfSerialPorts()
19
+ self.populateBaudRateCombobox()
20
+ self.populateDataBitsCombobox()
21
+ self.populateParityCombobox()
22
+ self.populateStopBitsCombobox()
23
+
24
+ self.connectWidget.pb_refresh.clicked.connect(self.refreshListOfSerialPorts)
25
+ self.connectWidget.buttonBox.accepted.connect(self.accept)
26
+ self.connectWidget.buttonBox.rejected.connect(self.reject)
27
+
28
+ QVBoxLayout(self).addWidget(self.connectWidget)
29
+
30
+ def disablePorts(self, disabled_port_names: list):
31
+ self.disabled_port_names = disabled_port_names
32
+ self.refreshListOfSerialPorts()
33
+
34
+ @Slot()
35
+ def refreshListOfSerialPorts(self):
36
+ port_name_list = [p.device for p in serial.tools.list_ports.comports() if
37
+ p.device not in self.disabled_port_names]
38
+ self.connectWidget.cb_portName.clear()
39
+ self.connectWidget.cb_portName.addItems(port_name_list)
40
+
41
+ ok_button_enabled_state = len(port_name_list) > 0
42
+ self.connectWidget.buttonBox.button(QDialogButtonBox.Ok).setEnabled(ok_button_enabled_state)
43
+
44
+ def populateBaudRateCombobox(self):
45
+ baudrates = ['9600', '38400', '115200', '256000', '1000000']
46
+ self.connectWidget.cb_baudrate.clear()
47
+ self.connectWidget.cb_baudrate.addItems(baudrates)
48
+ self.connectWidget.cb_baudrate.setCurrentIndex(4)
49
+
50
+ def populateDataBitsCombobox(self):
51
+ self.connectWidget.cb_dataSize.clear()
52
+ self.connectWidget.cb_dataSize.addItem(str(serial.FIVEBITS), userData=serial.FIVEBITS)
53
+ self.connectWidget.cb_dataSize.addItem(str(serial.SIXBITS), userData=serial.SIXBITS)
54
+ self.connectWidget.cb_dataSize.addItem(str(serial.SEVENBITS), userData=serial.SEVENBITS)
55
+ self.connectWidget.cb_dataSize.addItem(str(serial.EIGHTBITS), userData=serial.EIGHTBITS)
56
+ self.connectWidget.cb_dataSize.setCurrentIndex(3)
57
+
58
+ def populateParityCombobox(self):
59
+ self.connectWidget.cb_parity.clear()
60
+ self.connectWidget.cb_parity.addItem(serial.PARITY_NAMES[serial.PARITY_NONE], userData=serial.PARITY_NONE)
61
+ self.connectWidget.cb_parity.addItem(serial.PARITY_NAMES[serial.PARITY_EVEN], userData=serial.PARITY_EVEN)
62
+ self.connectWidget.cb_parity.addItem(serial.PARITY_NAMES[serial.PARITY_ODD], userData=serial.PARITY_ODD)
63
+ self.connectWidget.cb_parity.addItem(serial.PARITY_NAMES[serial.PARITY_MARK], userData=serial.PARITY_MARK)
64
+ self.connectWidget.cb_parity.addItem(serial.PARITY_NAMES[serial.PARITY_SPACE], userData=serial.PARITY_SPACE)
65
+ self.connectWidget.cb_parity.setCurrentIndex(0)
66
+
67
+ def populateStopBitsCombobox(self):
68
+ self.connectWidget.cb_stopBits.clear()
69
+ self.connectWidget.cb_stopBits.addItem(str(serial.STOPBITS_ONE), userData=serial.STOPBITS_ONE)
70
+ self.connectWidget.cb_stopBits.addItem(str(serial.STOPBITS_ONE_POINT_FIVE),
71
+ userData=serial.STOPBITS_ONE_POINT_FIVE)
72
+ self.connectWidget.cb_stopBits.addItem(str(serial.STOPBITS_TWO), userData=serial.STOPBITS_TWO)
73
+ self.connectWidget.cb_stopBits.setCurrentIndex(0)
74
+
75
+ def getName(self):
76
+ name = self.connectWidget.ed_name.text()
77
+ if name == '':
78
+ name = self.getPortName()
79
+ elif self.getPortName() not in name:
80
+ name = '{} ({})'.format(name, self.getPortName())
81
+ return name
82
+
83
+ def getPortName(self):
84
+ return self.connectWidget.cb_portName.currentText()
85
+
86
+ def getBaudrate(self):
87
+ return self.connectWidget.cb_baudrate.currentText()
88
+
89
+ def getDataBits(self):
90
+ return self.connectWidget.cb_dataSize.currentData()
91
+
92
+ def getParity(self):
93
+ return self.connectWidget.cb_parity.currentData()
94
+
95
+ def getStopBits(self):
96
+ return self.connectWidget.cb_stopBits.currentData()
@@ -0,0 +1,67 @@
1
+ from PySide6.QtCore import Qt, Slot, Signal
2
+ from PySide6.QtGui import QTextCursor, QClipboard
3
+ from PySide6.QtWidgets import QApplication, QMdiSubWindow, QTextEdit, QPushButton, QCheckBox
4
+ from typing import List
5
+ from multiserialviewer.text_highlighter.textHighlighter import TextHighlighter, TextHighlighterConfig
6
+ from multiserialviewer.ui_files.uiFileHelper import createWidgetFromUiFile
7
+
8
+
9
+ class SerialViewerWindow(QMdiSubWindow):
10
+ closed = Signal()
11
+
12
+ def __init__(self, window_title):
13
+ super().__init__()
14
+
15
+ widget = createWidgetFromUiFile("serialViewerWindow.ui")
16
+
17
+ self.setWidget(widget)
18
+ self.setWindowTitle(window_title)
19
+ self.setAttribute(Qt.WA_DeleteOnClose)
20
+
21
+ self.textEdit: QTextEdit = widget.findChild(QTextEdit, 'textEdit')
22
+
23
+ self.highlighter = TextHighlighter()
24
+ self.highlighter.setDocument(self.textEdit.document())
25
+
26
+ pb_clear: QPushButton = widget.findChild(QPushButton, 'pb_clear')
27
+ pb_clear.pressed.connect(self.clear)
28
+
29
+ pb_copy: QPushButton = widget.findChild(QPushButton, 'pb_copy')
30
+ pb_copy.pressed.connect(self.copy)
31
+
32
+ self.checkBox_enabled: QCheckBox = self.widget().findChild(QCheckBox, 'checkBox_enabled')
33
+
34
+ def closeEvent(self, event):
35
+ # is not called when mainwindow is closed
36
+ event.accept()
37
+ self.closed.emit()
38
+
39
+ @Slot()
40
+ def clear(self):
41
+ self.textEdit.clear()
42
+
43
+ def setHighlighterSettings(self, settings: List[TextHighlighterConfig]):
44
+ self.highlighter.setSettings(settings)
45
+ self.highlighter.rehighlight()
46
+
47
+ @Slot()
48
+ def copy(self):
49
+ clipboard: QClipboard = QApplication.clipboard()
50
+ cursor = self.textEdit.textCursor()
51
+
52
+ if cursor.selection().isEmpty():
53
+ text = self.textEdit.toPlainText()
54
+ else:
55
+ # copy selected text
56
+ text = cursor.selection().toPlainText()
57
+
58
+ if len(text) > 0:
59
+ clipboard.setText(text)
60
+
61
+ @Slot()
62
+ def appendData(self, data, force=False):
63
+ if self.checkBox_enabled.isChecked() or force:
64
+ self.textEdit.moveCursor(QTextCursor.End)
65
+ self.textEdit.insertPlainText(data)
66
+ self.textEdit.ensureCursorVisible()
67
+
@@ -0,0 +1,46 @@
1
+ from PySide6.QtWidgets import QDialog, QVBoxLayout, QHeaderView
2
+ from typing import List
3
+
4
+ from multiserialviewer.ui_files.uiFileHelper import createWidgetFromUiFile
5
+ from multiserialviewer.text_highlighter.textHighlighterConfig import TextHighlighterConfig
6
+ from multiserialviewer.text_highlighter.textHighlighterTableModel import TextHighlighterTableModel
7
+ from multiserialviewer.text_highlighter.colorSelectorItemDelegate import ColorSelectorItemDelegate
8
+
9
+
10
+ class TextHighlighterSettingsDialog(QDialog):
11
+ def __init__(self, parent, settings: List[TextHighlighterConfig]):
12
+ super().__init__(parent)
13
+
14
+ self.setWindowTitle("Text Highlighter Settings")
15
+ self.widget = createWidgetFromUiFile("textHighlighterSettingsDialog.ui")
16
+
17
+ self.table_model = TextHighlighterTableModel(settings)
18
+ self.widget.tableView.setModel(self.table_model)
19
+ self.widget.tableView.setItemDelegateForColumn(1, ColorSelectorItemDelegate(self.widget.tableView))
20
+ self.widget.tableView.setItemDelegateForColumn(2, ColorSelectorItemDelegate(self.widget.tableView))
21
+
22
+ # QTableView Headers
23
+ self.horizontal_header = self.widget.tableView.horizontalHeader()
24
+ self.vertical_header = self.widget.tableView.verticalHeader()
25
+
26
+ # size
27
+ self.horizontal_header.setSectionResizeMode(QHeaderView.Stretch)
28
+ self.horizontal_header.setSectionResizeMode(3, QHeaderView.ResizeToContents)
29
+ self.horizontal_header.setSectionResizeMode(4, QHeaderView.ResizeToContents)
30
+
31
+ # buttons
32
+ self.widget.buttonBox.accepted.connect(self.accept)
33
+ self.widget.buttonBox.rejected.connect(self.reject)
34
+ self.widget.pb_add.clicked.connect(self.addSetting)
35
+ self.widget.pb_delete.clicked.connect(self.deleteSetting)
36
+
37
+ QVBoxLayout(self).addWidget(self.widget)
38
+
39
+ def addSetting(self):
40
+ self.table_model.insertRows(self.table_model.rowCount(), 1)
41
+
42
+ def deleteSetting(self):
43
+ selected_model_indices = [modelIndex.row() for modelIndex in self.widget.tableView.selectionModel().selectedRows()]
44
+
45
+ for index in sorted(selected_model_indices, reverse=True):
46
+ self.table_model.removeRows(index, 1)
File without changes
@@ -0,0 +1,11 @@
1
+ import serial
2
+
3
+
4
+ class SerialConnectionSettings:
5
+ def __init__(self, port_name: str):
6
+ self.portName = port_name
7
+ self.baudrate = 1000000
8
+ self.bytesize = serial.EIGHTBITS
9
+ self.parity = serial.PARITY_NONE
10
+ self.stopbits = serial.STOPBITS_ONE
11
+ self.timeout = 0.5
@@ -0,0 +1,51 @@
1
+ import time
2
+ from threading import Thread, Event
3
+ from queue import Empty
4
+ from PySide6.QtCore import Signal, QObject
5
+
6
+
7
+ class SerialDataProcessor(QObject):
8
+ dataAvailable = Signal(str)
9
+
10
+ def __init__(self, raw_data_queue):
11
+ super(SerialDataProcessor, self).__init__()
12
+ self.lastEmitTimestamp = self.getTimestamp()
13
+ self.terminateEvent = Event()
14
+ self.rawDataQueue = raw_data_queue
15
+ self.thread = None
16
+
17
+ def start(self):
18
+ if self.thread is None:
19
+ self.thread = Thread(target=self.processData, args=(self.rawDataQueue, self.terminateEvent))
20
+ self.thread.start()
21
+
22
+ def stop(self):
23
+ if self.thread and self.thread.is_alive():
24
+ self.terminateEvent.set()
25
+ self.thread.join()
26
+ self.thread = None
27
+ self.terminateEvent.clear()
28
+
29
+ def getTimestamp(self):
30
+ return int(round(time.time() * 1000))
31
+
32
+ def timeDiffSinceLastEmit(self):
33
+ return self.getTimestamp() - self.lastEmitTimestamp
34
+
35
+ def processData(self, queue, terminate_event):
36
+ data = bytearray()
37
+
38
+ while not terminate_event.is_set():
39
+ try:
40
+ rx_bytes = queue.get(timeout=0.2)
41
+ data.extend(rx_bytes)
42
+ # queue.task_done()
43
+ except Empty:
44
+ pass
45
+ finally:
46
+ if len(data) > 0:
47
+ try:
48
+ self.dataAvailable.emit(data.decode("ascii"))
49
+ except:
50
+ pass
51
+ data = bytearray()