This commit is contained in:
2025-04-19 23:06:54 +02:00
parent e4663df500
commit 05abcca6f5
5 changed files with 143 additions and 59 deletions

View File

@@ -3,30 +3,42 @@ from tkinter import ttk
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import asyncio
from .filter import * from .filter import *
from .gui.device import Device from .gui.device import Device
from .gui.slider import Slider
class FilterDevApp(tk.Tk):
def __init__(self, loop: asyncio.EventLoop):
super().__init__()
self.loop = loop
self.protocol("WM_DELETE_WINDOW", self.close)
self.tasks = []
self.tasks.append(loop.create_task(self.updater(1./2)))
self.tasks.append(loop.create_task(self.update_plot(1./20)))
self.tasks.append(loop.create_task(self.read_values(1./100)))
class FilterDevApp:
def __init__(self, root):
self.filter = None self.filter = None
self.root = root self.title("JannTers Filter Evaluation Tool")
self.root.title("JannTers Filter Evaluation Tool")
# Create a frame for the plot and sliders # Create a frame for the plot and sliders
self.frame = tk.Frame(self.root) self.frame = tk.Frame(self)
self.frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) self.frame.pack(side=tk.LEFT, fill=tk.BOTH)
# Create a figure for plotting # Create a figure for plotting
self.fig, self.ax = plt.subplots() self.fig, self.ax = plt.subplots()
self.canvas = FigureCanvasTkAgg(self.fig, master=self.frame) self.canvas = FigureCanvasTkAgg(self.fig, master=self.frame)
self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=True) self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH)
# Create a frame for sliders # Create a frame for sliders
self.toolbar = tk.Frame(self.root) self.toolbar = tk.Frame(self, width=200, padx=10)
self.toolbar.pack(side=tk.RIGHT, fill=tk.Y) self.toolbar.pack(side=tk.RIGHT, fill=tk.Y)
# Device Settings # Device Settings
self.connect_button = ttk.Button(self.toolbar, text="Connect", command=self.connect_disconnect)
self.connect_button.pack(pady=10)
self.device_label = ttk.Label(self.toolbar, text="Device Name:") self.device_label = ttk.Label(self.toolbar, text="Device Name:")
self.device_label.pack(pady=10) self.device_label.pack(pady=10)
self.device_name = ttk.Entry(self.toolbar) self.device_name = ttk.Entry(self.toolbar)
@@ -35,9 +47,6 @@ class FilterDevApp:
self.device = Device(self.device_name) self.device = Device(self.device_name)
self.connect_button = ttk.Button(self.toolbar, text="Connect Device", command=self.device.connect)
self.connect_button.pack(pady=10)
# Filter Settings # Filter Settings
self.filter_type_label = ttk.Label(self.toolbar, text="Filter:") self.filter_type_label = ttk.Label(self.toolbar, text="Filter:")
self.filter_type_label.pack(pady=10) self.filter_type_label.pack(pady=10)
@@ -48,35 +57,34 @@ class FilterDevApp:
self.change_filter.pack(pady=10) self.change_filter.pack(pady=10)
# Objects # Objects
self.filter = MovAvg(self.device, self.toolbar, self.update_plot) self.filter = MovAvg(self.device, self.toolbar, lambda: None)
self.filter.pack()
# Initial plot async def update_plot(self, interval):
self.update_plot() while await asyncio.sleep(interval, True):
if self.filter is None:
continue
def update_plot(self, *args): # Clear the current plot
if self.filter is None: self.ax.clear()
return
# Clear the current plot # Get current values from sliders
self.ax.clear() df = self.filter()
# Get current values from sliders # Generate data
df = self.filter() x = df['timestamps']
y1 = df['weights']
y2 = df['filtered']
# Generate data # Plot the data
x = df['timestamps'] self.ax.plot(x, y1)
y1 = df['weights'] self.ax.plot(x, y2)
y2 = df['filtered'] self.ax.set_xlabel("Time in ms")
self.ax.set_ylabel("Weight")
self.ax.grid()
# Plot the data # Draw the updated plot
self.ax.plot(x, y1) self.canvas.draw()
self.ax.plot(x, y2)
self.ax.set_xlabel("Time in ms")
self.ax.set_ylabel("Weight")
self.ax.grid()
# Draw the updated plot
self.canvas.draw()
def update_filter(self): def update_filter(self):
option = self.filter_type_combobox.get() option = self.filter_type_combobox.get()
@@ -85,7 +93,45 @@ class FilterDevApp:
self.update_plot() self.update_plot()
def connect_disconnect(self):
if self.device.is_connected:
self.device_label.pack(pady=10)
self.device_name.pack(pady=10)
self.connect_button.config(text="Connect")
self.connect_button.pack(pady=10)
self.device.disconnect()
else:
task = self.loop.create_task(self.device.connect())
task.add_done_callback(self.connected)
def connected(self, *args):
if self.device.is_connected:
self.device_label.pack_forget()
self.device_name.pack_forget()
self.connect_button.config(text="Disconnect")
self.filter.pack()
async def read_values(self, interval):
await self.device.read_values(interval)
async def updater(self, interval):
while await asyncio.sleep(interval, True):
self.update()
def close(self):
for task in self.tasks:
task.cancel()
self.loop.stop()
self.destroy()
if __name__ == "__main__": if __name__ == "__main__":
root = tk.Tk() loop = asyncio.new_event_loop()
app = FilterDevApp(root) app = FilterDevApp(loop)
root.mainloop() loop.run_forever()
loop.close()

View File

@@ -6,8 +6,13 @@ import pandas as pd
from argparse import ArgumentParser from argparse import ArgumentParser
from tqdm.auto import tqdm from tqdm.auto import tqdm
from config import MILLIS_UUID, WEIGHT_UUID
READING_TIME = 30
async def read_ble(reading_time=READING_TIME): async def read_ble(reading_time=READING_TIME):
device = await BleakScanner.find_device_by_name("Smaage") device = await BleakScanner.find_device_by_name("Smaage")
assert device is not None, "No device found"
timestamps = [] timestamps = []
raw_weights = [] raw_weights = []
@@ -45,3 +50,11 @@ async def read_ble(reading_time=READING_TIME):
print(df['weights'].describe()) print(df['weights'].describe())
plt.plot(timestamps, raw_weights) plt.plot(timestamps, raw_weights)
plt.show() plt.show()
if __name__ == '__main__':
parser = ArgumentParser()
parser.add_argument('-t', default=READING_TIME, type=int, help="reading time in sec")
args = parser.parse_args()
asyncio.run(read_ble(args.t))

View File

@@ -34,3 +34,11 @@ class Filter:
def filter(self, df: pd.DataFrame) -> pd.Series: def filter(self, df: pd.DataFrame) -> pd.Series:
raise NotImplementedError() raise NotImplementedError()
def pack(self):
for k, v in self.param_map.items():
v.pack()
def pack_forget(self):
for k, v in self.param_map.items():
v.pack_forget()

View File

@@ -1,6 +1,7 @@
from bleak import BleakClient, BleakScanner from bleak import BleakClient, BleakScanner
import pandas as pd import pandas as pd
from tkinter.ttk import Entry from tkinter.ttk import Entry
import asyncio
from ..config import MILLIS_UUID, WEIGHT_UUID from ..config import MILLIS_UUID, WEIGHT_UUID
@@ -26,15 +27,28 @@ class Device:
async def connect(self): async def connect(self):
self.device = await BleakScanner.find_device_by_name(self.device_name.get()) self.device = await BleakScanner.find_device_by_name(self.device_name.get())
assert self.device is not None, "No Device found!" return self.device is not None
async def read_values(self): def disconnect(self):
assert self.is_connected, "Not connected" self.device = None
async with BleakClient(self.device.address) as client:
millis = await client.read_gatt_char(MILLIS_UUID)
millis = int.from_bytes(millis, byteorder='little') # Adjust based on your data format
weight = await client.read_gatt_char(WEIGHT_UUID)
weight = int.from_bytes(weight, byteorder='little') # Adjust based on your data format
self.timestamps.append(millis) async def read_values(self, interval):
self.weights.append(weight) while await asyncio.sleep(interval, True):
if self.is_connected:
async with BleakClient(self.device.address) as client:
while await asyncio.sleep(interval, True):
millis = await client.read_gatt_char(MILLIS_UUID)
millis = int.from_bytes(millis, byteorder='little') # Adjust based on your data format
weight = await client.read_gatt_char(WEIGHT_UUID)
weight = int.from_bytes(weight, byteorder='little') # Adjust based on your data format
self.timestamps.append(millis)
self.weights.append(weight)
if not self.is_connected:
break
def clear_data(self):
self.timestamps = []
self.weights = []

View File

@@ -9,15 +9,10 @@ class Slider:
command): command):
self.command = command self.command = command
self.frame = ttk.Frame(parent) self.label = ttk.Label(parent, text=f"{label_text}: {int(initial_value)}")
self.frame.pack(pady=10)
self.label = ttk.Label(self.frame, text=f"{label_text}: {int(initial_value)}") self.slider = ttk.Scale(parent, from_=from_, to=to, orient='horizontal', command=self.update)
self.label.pack()
self.slider = ttk.Scale(self.frame, from_=from_, to=to, orient='horizontal', command=self.update)
self.slider.set(initial_value) self.slider.set(initial_value)
self.slider.pack()
def update(self, event=None): def update(self, event=None):
value = self.slider.get() value = self.slider.get()
@@ -26,3 +21,11 @@ class Slider:
def get_value(self): def get_value(self):
return self.slider.get() return self.slider.get()
def pack(self, **kwargs):
self.label.pack(**kwargs)
self.slider.pack(**kwargs)
def pack_forget(self):
self.label.pack_forget()
self.slider.pack_forget()