Skip to content

Commit

Permalink
Add files via upload
Browse files Browse the repository at this point in the history
  • Loading branch information
AKalakota23 authored Nov 1, 2024
1 parent b0bb0ae commit d599c98
Showing 1 changed file with 117 additions and 0 deletions.
117 changes: 117 additions & 0 deletions UI.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import tkinter as tk
import tkinter.ttk as ttk
from serial import Serial
from serial.tools.list_ports import comports
from tkinter.messagebox import showerror
from threading import Thread, Lock, Event
from serial import Serial, SerialException
import sys

S_OK: int = 0xaa
S_ERR: int = 0xff

class LockedSerial(Serial):
_lock: Lock = Lock()

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

def read(self, size=1) -> bytes:
with self._lock:
return super().read(size)

def write(self, b: bytes, /) -> int | None:
with self._lock:
super().write(b)

def close(self):
with self._lock:
super().close()


class App(tk.Tk):
ser: LockedSerial
def __init__(self):
super().__init__()

self.title("LED Blinker")
self.protocol("WM_DELETE_WINDOW", self.on_close) # Bind close button to on_close
self.stop_event = Event()
self.port = tk.StringVar()
self.led = tk.BooleanVar()

ttk.Checkbutton(self, text='Toggle LED', variable=self.led, command=self.update_led).pack()
ttk.Button(self, text='Send Invalid', command=self.send_invalid).pack()
ttk.Button(self, text='Disconnect', command=self.disconnect,default='active').pack()

SerialPortal(self)

def detached_callback(f):
return lambda *args, **kwargs: Thread(target=f, args=args, kwargs=kwargs).start()

def __enter__(self):
return self

def __exit__(self, *_):
self.disconnect()

def disconnect(self):
self.ser.close()

SerialPortal(self)

def send_invalid(self):
self.write(bytes([0x10]))

def connect(self):
self.ser = LockedSerial(self.port.get())

@detached_callback
def update_led(self):
#value = self.led.get()
self.write(bytes([self.led.get()]))

def send_invalid(self):
self.write(bytes([0x10]))

def write(self, b: bytes):
try:
self.ser.write(b)
if int.from_bytes(self.ser.read(), 'big') == S_ERR:
showerror('Device Error', 'The device reported an invalid command.')
except SerialException:
showerror('Serial Error', 'Write failed.')

def on_close(self):
self.stop_event.set() #signals threads to stop
self.disconnect()
self.destroy()
sys.exit()

class SerialPortal(tk.Toplevel):
def __init__(self, parent: App):
super().__init__(parent)

self.parent = parent
self.protocol("WM_DELETE_WINDOW", self.on_close) # Bind close button to on_close
self.parent.withdraw() # hide App until connected

ttk.OptionMenu(self, parent.port, '', *[d.device for d in comports()]).pack()
ttk.Button(self, text='Connect', command=self.connect, default='active').pack()

def connect(self):
self.parent.connect()
self.destroy()
self.parent.deiconify() # reveal App
# send `value` somehow??

def on_close(self):
"""Close the SerialPortal window and terminate the application."""
self.parent.on_close() # Call the main app's on_close method to ensure cleanup
self.destroy() # Ensure the SerialPortal window is closed


if __name__ == '__main__':
with App() as app:
app.mainloop()

0 comments on commit d599c98

Please sign in to comment.