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,14 +57,13 @@ 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):
def update_plot(self, *args):
if self.filter is None: if self.filter is None:
return continue
# Clear the current plot # Clear the current plot
self.ax.clear() self.ax.clear()
@@ -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,11 +27,16 @@ 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 def read_values(self, interval):
while await asyncio.sleep(interval, True):
if self.is_connected:
async with BleakClient(self.device.address) as client: async with BleakClient(self.device.address) as client:
while await asyncio.sleep(interval, True):
millis = await client.read_gatt_char(MILLIS_UUID) millis = await client.read_gatt_char(MILLIS_UUID)
millis = int.from_bytes(millis, byteorder='little') # Adjust based on your data format millis = int.from_bytes(millis, byteorder='little') # Adjust based on your data format
weight = await client.read_gatt_char(WEIGHT_UUID) weight = await client.read_gatt_char(WEIGHT_UUID)
@@ -38,3 +44,11 @@ class Device:
self.timestamps.append(millis) self.timestamps.append(millis)
self.weights.append(weight) 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()