import Serial_decoder as SD import tkinter as tk from tkinter import ttk from datetime import datetime import time from collections import deque # Matplotlib embedding in Tkinter from matplotlib.figure import Figure from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg # === Serial threads (unchanged) === tasks = SD.StartInputOutput() # t1: output_loop (daemon), t2: input_loop (daemon) FILENAME = f"SaveData/{datetime.today().strftime('%Y_%m_%d_%H_%M')}" # === Plot history === MAX_POINTS = 100 # rolling window length history_x = deque(maxlen=MAX_POINTS) # timestamps history_y = [deque(maxlen=MAX_POINTS) for _ in range(16)] # per-channel values # === GUI setup === root = tk.Tk() root.title("Live 4x4 Grid + Selectable Real-Time Plot") # Left: 4x4 grid grid_frame = ttk.Frame(root) grid_frame.grid(row=0, column=0, padx=10, pady=10, sticky="n") labels = [] for row in range(4): for col in range(4): idx = row * 4 + col lbl = tk.Label( grid_frame, name=f"channel{idx}", text="0", width=10, height=3, borderwidth=2, relief="groove", font=("Arial", 14) ) lbl.grid(row=row, column=col, padx=5, pady=5) labels.append(lbl) # Last tick label (as you had) last_tick_lbl = tk.Label( grid_frame, text="Last Tick: -", width=20, height=3, borderwidth=2, relief="groove", font=("Arial", 14) ) lbl = tk.Label(root, text="Last Tick:0", width=20, height=3, borderwidth=2, relief="groove", font=("Arial", 14)) lbl.grid(row = 4, column=0, columnspan=2) labels.append(lbl) lbl = tk.Label(root, text="Delta Tick:0", width=20, height=3, borderwidth=2, relief="groove", font=("Arial", 14)) lbl.grid(row=4, column=2, columnspan= 2) labels.append(lbl) # Checkboxes to select plotted channels check_frame = ttk.LabelFrame(root, text="Plot Selection") check_frame.grid(row=1, column=0, padx=10, pady=(0, 10), sticky="nw") check_vars = [] for i in range(16): var = tk.IntVar(value=0) chk = ttk.Checkbutton(check_frame, text=f"Ch {i+1}", variable=var, command=lambda: update_plot()) chk.grid(row=i//4, column=i%4, sticky="w", padx=4, pady=2) check_vars.append(var) # Right: Matplotlib plot plot_frame = ttk.Frame(root) plot_frame.grid(row=0, column=1, rowspan=2, padx=10, pady=10, sticky="n") fig = Figure(figsize=(6, 4), dpi=100) ax = fig.add_subplot(111) ax.set_title("Selected Channels (rolling)") ax.set_xlabel("Timestamp") ax.set_ylabel("Value") canvas = FigureCanvasTkAgg(fig, master=plot_frame) canvas.get_tk_widget().pack(fill="both", expand=True) # Optional controls controls_frame = ttk.Frame(plot_frame) controls_frame.pack(fill="x", pady=5) # Clear selection button def clear_selection(): for v in check_vars: v.set(0) update_plot() ttk.Button(controls_frame, text="Clear Selection", command=clear_selection).pack(side="left") # === Update functions === Last_tick: int = 0 def update_grid(values: list,tick: int): global Last_tick with open(f"{FILENAME}_ADC.csv","+a") as file: file.write(f"{tick}") for i in range(16): file.write(f",{values[i]}") labels[i].config(text=f"{values[i]:n}") labels[16].config(text=f"Last Tick:{tick:n}") labels[17].config(text=f"Delta Tick:{(tick-Last_tick):n}") Last_tick = tick file.write("\n") def update_SI(value:SD.ReturnDecoder): with open(f"{FILENAME}_SI.csv","+a") as file: file.write(f"{value.timestamp},{value.log_message}\n") print(f"\033[F\033[2Ktime:{value.timestamp}, SI:{value.log_message}",end="\n") def append_history(adc_values: list, timestamp: int): """Store the new sample in the rolling history.""" history_x.append(timestamp) for i in range(16): history_y[i].append(adc_values[i]) def update_plot(): """Redraw plot based on selected channels and available history.""" ax.clear() ax.set_title("Selected Channels (rolling)") ax.set_xlabel("Timestamp") ax.set_ylabel("Value") selected = [i for i, var in enumerate(check_vars) if var.get() == 1] if len(history_x) == 0 or len(selected) == 0: ax.text(0.5, 0.5, "No data / No channels selected", ha="center", va="center", transform=ax.transAxes) canvas.draw() return x = list(history_x) for idx in selected: y = list(history_y[idx]) ax.plot(x, y, label=f"Ch {idx+1}") ax.legend(loc="upper left", fontsize=8) ax.grid(True, linestyle="--", alpha=0.3) canvas.draw() def update_panel(): """Main polling loop that consumes Serial_decoder outputs (unchanged structure).""" values: SD.ReturnDecoder = SD.GetReturn(0.1) # non-blocking read with timeout if values is None: root.after(10, update_panel) return # Stop if input thread died if tasks[1].is_alive() == False: root.quit() match values.name: case "ADC_FULLRANK": # Update grid + history + plot update_grid(values=values.ADC_data, tick=values.timestamp) append_history(values.ADC_data, values.timestamp) update_plot() # draw right away; if too heavy, throttle with a timer case "SI_DECODER": update_SI(values) case "PR_DECODER": print(f"PRD:{values.text_respons}") case _: print(f"Error {values.name}") # Continue polling root.after(10, update_panel) # 10 ms # Start polling and GUI loop update_panel() # FC commands SD.SerialPort.write(b"SET_ST_SPEED 100\n\r") root.mainloop()