GUI: Outsource tabs, Add tab digital_plot

This commit is contained in:
jonny 2025-01-30 13:59:56 +01:00
parent 48bfbb98ef
commit bb84205531
4 changed files with 178 additions and 115 deletions

74
rpi-scripts/gui/main.py Executable file → Normal file
View File

@ -1,16 +1,17 @@
import os
import sys
import time
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
import RPi.GPIO as GPIO
# Add the parent directory to the module search path
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
from interface_board_libs.adc_mcp3208 import MCP3208
from interface_board_libs.shift_register import ShiftRegister
from interface_board_pins import * # Import pin assignments
from gui_helpers import create_adc_plot
from tab_control import create_control_tab
from tab_adc_plot import create_adc_plot_tab
from tab_digital_plot import create_digital_plot_tab
# Initialize ADC & Shift Register
adc = MCP3208()
@ -35,71 +36,12 @@ root.configure(bg="black")
# Tabbed Interface
notebook = ttk.Notebook(root)
main_frame = ttk.Frame(notebook)
plot_frame = ttk.Frame(notebook)
notebook.add(main_frame, text="Controls")
notebook.add(plot_frame, text="ADC Plot")
notebook.pack(expand=True, fill="both")
# Layout Variables
digital_input_states = [tk.StringVar(value="LOW") for _ in range(8)]
digital_output_states = [tk.BooleanVar(value=False) for _ in range(8)]
adc_values = [tk.StringVar(value="0.00V") for _ in range(8)]
pwm_values = [tk.IntVar(value=0), tk.IntVar(value=0)]
def update_inputs():
for i, pin in enumerate(GPIO_DIGITAL_INPUTS.values()):
digital_input_states[i].set("HIGH" if GPIO.input(pin) else "LOW")
root.after(500, update_inputs)
def update_adc():
for i, adc_channel in enumerate(ADC_CHANNELS.values()):
value = adc.read(adc_channel)
adc_values[i].set(f"{round(value * 12 / 4095, 2)}V")
root.after(1000, update_adc)
def toggle_output(index):
shift_reg.set_pin(index, digital_output_states[index].get())
def update_pwm(channel, value):
duty_cycle = int(float(value))
if channel == 0:
pwm1.ChangeDutyCycle(duty_cycle)
else:
pwm2.ChangeDutyCycle(duty_cycle)
def exit_program():
pwm1.stop()
pwm2.stop()
GPIO.cleanup()
root.quit()
# Create UI Elements in Main Frame
frame = ttk.Frame(main_frame, padding=20)
frame.pack(expand=True, fill="both")
for i in range(8):
ttk.Label(frame, text=f"ADC {i+1}:").grid(row=i, column=0, sticky="e")
ttk.Label(frame, textvariable=adc_values[i], width=10).grid(row=i, column=1, sticky="w")
ttk.Label(frame, text=f"IN {i+1}:").grid(row=i, column=2, sticky="e")
ttk.Label(frame, textvariable=digital_input_states[i], width=6).grid(row=i, column=3, sticky="w")
btn = ttk.Checkbutton(frame, text=f"OUT {i+1}", variable=digital_output_states[i], command=lambda i=i: toggle_output(i))
btn.grid(row=i, column=4, sticky="w")
for i in range(2):
ttk.Label(frame, text=f"PWM{i+1}").grid(row=i, column=5, sticky="e")
slider = ttk.Scale(frame, from_=0, to=100, orient="horizontal", length=300, variable=pwm_values[i], command=lambda val, i=i: update_pwm(i, val))
slider.grid(row=i, column=6, sticky="w")
exit_button = ttk.Button(root, text="Exit", command=exit_program)
exit_button.place(relx=0.9, rely=0.9, anchor="center")
# Create ADC Plot in Second Tab
create_adc_plot(plot_frame, adc)
# Start Updates
update_inputs()
update_adc()
# Add tabs
create_control_tab(notebook, adc, shift_reg, pwm1, pwm2)
create_adc_plot_tab(notebook, adc)
create_digital_plot_tab(notebook)
# Run GUI
root.mainloop()

View File

@ -1,13 +1,16 @@
import tkinter as tk
from tkinter import ttk
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import time
def create_adc_plot(frame, adc):
"""
Creates a live-updating ADC plot in the given Tkinter frame.
"""
figure = Figure(figsize=(6, 4), dpi=100)
ADC_PLOT_UPDATE_INTERVAL = 100
def create_adc_plot_tab(notebook, adc):
frame = ttk.Frame(notebook)
notebook.add(frame, text="ADC Plot")
figure = Figure(figsize=(8, 5), dpi=100)
ax = figure.add_subplot(1, 1, 1)
ax.set_title("ADC Readings Over Time")
ax.set_xlabel("Time (s)")
@ -44,6 +47,6 @@ def create_adc_plot(frame, adc):
ax.legend(loc="upper right")
canvas.draw()
frame.after(1000, update_plot)
frame.after(ADC_PLOT_UPDATE_INTERVAL, update_plot)
update_plot()

View File

@ -0,0 +1,62 @@
import sys
import os
import tkinter as tk
from tkinter import ttk
import RPi.GPIO as GPIO
# Add the parent directory to the module search path
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
from interface_board_pins import * # Import pin assignments
def create_control_tab(notebook, adc, shift_reg, pwm1, pwm2):
frame = ttk.Frame(notebook)
notebook.add(frame, text="Controls")
digital_input_states = [tk.StringVar(value="LOW") for _ in range(8)]
digital_output_states = [tk.BooleanVar(value=False) for _ in range(8)]
adc_values = [tk.StringVar(value="0.00V") for _ in range(8)]
pwm_values = [tk.IntVar(value=0), tk.IntVar(value=0)]
def update_inputs():
for i, pin in enumerate(GPIO_DIGITAL_INPUTS.values()):
digital_input_states[i].set("HIGH" if GPIO.input(pin) else "LOW")
frame.after(500, update_inputs)
def update_adc():
for i, adc_channel in enumerate(ADC_CHANNELS.values()):
value = adc.read(adc_channel)
adc_values[i].set(f"{round(value * 12 / 4095, 2)}V")
frame.after(1000, update_adc)
def toggle_output(index):
shift_reg.set_pin(index, digital_output_states[index].get())
def update_pwm(channel, value):
duty_cycle = int(float(value))
if channel == 0:
pwm1.ChangeDutyCycle(duty_cycle)
else:
pwm2.ChangeDutyCycle(duty_cycle)
# UI Layout
style = ttk.Style()
style.configure("TScale", thickness=30) # Increases slider thickness
control_frame = ttk.Frame(frame, padding=30)
control_frame.pack(expand=True, fill="both")
for i in range(8):
ttk.Label(control_frame, text=f"ADC {i+1}:", font=("Arial", 14)).grid(row=i, column=0, sticky="e")
ttk.Label(control_frame, textvariable=adc_values[i], width=10, font=("Arial", 14)).grid(row=i, column=1, sticky="w")
ttk.Label(control_frame, text=f"IN {i+1}:", font=("Arial", 14)).grid(row=i, column=2, sticky="e")
ttk.Label(control_frame, textvariable=digital_input_states[i], width=6, font=("Arial", 14)).grid(row=i, column=3, sticky="w")
btn = ttk.Checkbutton(control_frame, text=f"OUT {i+1}", variable=digital_output_states[i], command=lambda i=i: toggle_output(i))
btn.grid(row=i, column=4, sticky="w")
for i in range(2):
ttk.Label(control_frame, text=f"PWM{i+1}:", font=("Arial", 14)).grid(row=i, column=5, sticky="e")
slider = ttk.Scale(control_frame, from_=0, to=100, orient="horizontal", length=400, variable=pwm_values[i], command=lambda val, i=i: update_pwm(i, val), style="TScale")
slider.grid(row=i, column=6, sticky="w", pady=10) # Added spacing with `pady=10`
update_inputs()
update_adc()

View File

@ -0,0 +1,56 @@
import sys
import os
import tkinter as tk
from tkinter import ttk
import RPi.GPIO as GPIO
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import time
# Add the parent directory to the module search path
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
from interface_board_pins import * # Import pin assignments
def create_digital_plot_tab(notebook):
frame = ttk.Frame(notebook)
notebook.add(frame, text="Digital Inputs")
figure = Figure(figsize=(8, 5), dpi=100)
ax = figure.add_subplot(1, 1, 1)
ax.set_title("Digital Input States Over Time")
ax.set_xlabel("Time (s)")
ax.set_ylabel("State (0=LOW, 1=HIGH)")
ax.set_ylim(-0.2, 1.2)
canvas = FigureCanvasTkAgg(figure, master=frame)
canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)
input_channels = list(range(8))
data = {ch: [] for ch in input_channels}
time_data = []
def update_plot():
current_time = time.time()
if len(time_data) > 50:
for ch in input_channels:
data[ch].pop(0)
time_data.pop(0)
time_data.append(current_time)
for ch in input_channels:
state = GPIO.input(GPIO_DIGITAL_INPUTS[ch])
data[ch].append(state)
ax.clear()
ax.set_title("Digital Input States Over Time")
ax.set_xlabel("Time (s)")
ax.set_ylabel("State (0=LOW, 1=HIGH)")
ax.set_ylim(-0.2, 1.2)
for ch in input_channels:
ax.step(time_data, data[ch], label=f"IN {ch+1}", where="post")
ax.legend(loc="upper right")
canvas.draw()
frame.after(500, update_plot)
update_plot()