btclock-ota-flasher/app/gui/serial_monitor.py
2025-01-05 16:26:28 +01:00

126 lines
No EOL
4.6 KiB
Python

import wx
import serial
import threading
import time
class SerialMonitor(wx.Dialog):
def __init__(self, parent, port):
super().__init__(parent, title=f"Serial Monitor - {port}", size=(600, 400))
self.port = port
self.serial = None
self.running = False
self._destroyed = False
# Create UI elements
panel = wx.Panel(self)
vbox = wx.BoxSizer(wx.VERTICAL)
# Text area for output
self.text_area = wx.TextCtrl(panel, style=wx.TE_MULTILINE | wx.TE_READONLY | wx.HSCROLL)
vbox.Add(self.text_area, 1, wx.EXPAND | wx.ALL, 5)
# Input area
hbox = wx.BoxSizer(wx.HORIZONTAL)
self.input_field = wx.TextCtrl(panel, style=wx.TE_PROCESS_ENTER)
self.input_field.Bind(wx.EVT_TEXT_ENTER, self.on_send)
hbox.Add(self.input_field, 1, wx.EXPAND | wx.RIGHT, 5)
# Send button
send_button = wx.Button(panel, label="Send")
send_button.Bind(wx.EVT_BUTTON, self.on_send)
hbox.Add(send_button, 0)
vbox.Add(hbox, 0, wx.EXPAND | wx.ALL, 5)
# Baud rate selector
hbox2 = wx.BoxSizer(wx.HORIZONTAL)
hbox2.Add(wx.StaticText(panel, label="Baud Rate:"), 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 5)
self.baud_selector = wx.Choice(panel, choices=["9600", "115200"])
self.baud_selector.SetSelection(1) # Default to 115200
self.baud_selector.Bind(wx.EVT_CHOICE, self.on_baud_change)
hbox2.Add(self.baud_selector, 0)
# Clear button
clear_button = wx.Button(panel, label="Clear")
clear_button.Bind(wx.EVT_BUTTON, self.on_clear)
hbox2.Add(clear_button, 0, wx.LEFT, 5)
vbox.Add(hbox2, 0, wx.EXPAND | wx.ALL, 5)
panel.SetSizer(vbox)
# Bind close event
self.Bind(wx.EVT_CLOSE, self.on_close)
self.start_monitor()
def start_monitor(self):
try:
baud = int(self.baud_selector.GetString(self.baud_selector.GetSelection()))
self.serial = serial.Serial(self.port, baud, timeout=0.1)
self.running = True
self.monitor_thread = threading.Thread(target=self.read_serial)
self.monitor_thread.daemon = True
self.monitor_thread.start()
except Exception as e:
wx.MessageBox(f"Error opening port: {str(e)}", "Error", wx.OK | wx.ICON_ERROR)
self.Close()
def read_serial(self):
while self.running:
if self.serial and self.serial.is_open:
try:
if self.serial.in_waiting:
data = self.serial.read(self.serial.in_waiting)
if data and not self._destroyed:
wx.CallAfter(self.append_text, data.decode('utf-8', errors='replace'))
except Exception as e:
if not self._destroyed:
wx.CallAfter(self.append_text, f"\nError reading from port: {str(e)}\n")
break
time.sleep(0.1)
def append_text(self, text):
if not self._destroyed and self and self.text_area:
try:
self.text_area.AppendText(text)
except:
pass # Ignore any errors if the window is being destroyed
def on_send(self, event):
if self.serial and self.serial.is_open and not self._destroyed:
text = self.input_field.GetValue() + '\n'
try:
self.serial.write(text.encode())
self.input_field.SetValue("")
except Exception as e:
if not self._destroyed:
wx.MessageBox(f"Error sending data: {str(e)}", "Error", wx.OK | wx.ICON_ERROR)
def on_baud_change(self, event):
if self.serial and not self._destroyed:
try:
self.serial.close()
self.start_monitor()
except Exception as e:
if not self._destroyed:
wx.MessageBox(f"Error changing baud rate: {str(e)}", "Error", wx.OK | wx.ICON_ERROR)
def on_clear(self, event):
if not self._destroyed and self.text_area:
self.text_area.SetValue("")
def on_close(self, event):
self.running = False
self._destroyed = True
if self.serial:
try:
self.serial.close()
except:
pass # Ignore any errors during cleanup
event.Skip()
def Destroy(self):
self._destroyed = True
super().Destroy()