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 os
import sys import sys
import time
import tkinter as tk import tkinter as tk
from tkinter import ttk from tkinter import ttk
from tkinter import messagebox
import RPi.GPIO as GPIO import RPi.GPIO as GPIO
# Add the parent directory to the module search path # Add the parent directory to the module search path
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) 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.adc_mcp3208 import MCP3208
from interface_board_libs.shift_register import ShiftRegister from interface_board_libs.shift_register import ShiftRegister
from interface_board_pins import * # Import pin assignments 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 # Initialize ADC & Shift Register
adc = MCP3208() adc = MCP3208()
@ -35,71 +36,12 @@ root.configure(bg="black")
# Tabbed Interface # Tabbed Interface
notebook = ttk.Notebook(root) 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") notebook.pack(expand=True, fill="both")
# Layout Variables # Add tabs
digital_input_states = [tk.StringVar(value="LOW") for _ in range(8)] create_control_tab(notebook, adc, shift_reg, pwm1, pwm2)
digital_output_states = [tk.BooleanVar(value=False) for _ in range(8)] create_adc_plot_tab(notebook, adc)
adc_values = [tk.StringVar(value="0.00V") for _ in range(8)] create_digital_plot_tab(notebook)
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()
# Run GUI # Run GUI
root.mainloop() root.mainloop()

View File

@ -1,49 +1,52 @@
import tkinter as tk import tkinter as tk
from matplotlib.figure import Figure from tkinter import ttk
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg from matplotlib.figure import Figure
import time from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import time
def create_adc_plot(frame, adc):
""" ADC_PLOT_UPDATE_INTERVAL = 100
Creates a live-updating ADC plot in the given Tkinter frame.
""" def create_adc_plot_tab(notebook, adc):
figure = Figure(figsize=(6, 4), dpi=100) frame = ttk.Frame(notebook)
ax = figure.add_subplot(1, 1, 1) notebook.add(frame, text="ADC Plot")
ax.set_title("ADC Readings Over Time")
ax.set_xlabel("Time (s)") figure = Figure(figsize=(8, 5), dpi=100)
ax.set_ylabel("Voltage (V)") ax = figure.add_subplot(1, 1, 1)
ax.set_ylim(0, 12) ax.set_title("ADC Readings Over Time")
ax.set_xlabel("Time (s)")
canvas = FigureCanvasTkAgg(figure, master=frame) ax.set_ylabel("Voltage (V)")
canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True) ax.set_ylim(0, 12)
adc_channels = list(range(8)) canvas = FigureCanvasTkAgg(figure, master=frame)
data = {ch: [] for ch in adc_channels} canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)
time_data = []
adc_channels = list(range(8))
def update_plot(): data = {ch: [] for ch in adc_channels}
current_time = time.time() time_data = []
if len(time_data) > 50:
for ch in adc_channels: def update_plot():
data[ch].pop(0) current_time = time.time()
time_data.pop(0) if len(time_data) > 50:
for ch in adc_channels:
time_data.append(current_time) data[ch].pop(0)
for ch in adc_channels: time_data.pop(0)
voltage = round(adc.read(ch) * 12 / 4095, 2)
data[ch].append(voltage) time_data.append(current_time)
for ch in adc_channels:
ax.clear() voltage = round(adc.read(ch) * 12 / 4095, 2)
ax.set_title("ADC Readings Over Time") data[ch].append(voltage)
ax.set_xlabel("Time (s)")
ax.set_ylabel("Voltage (V)") ax.clear()
ax.set_ylim(0, 12) ax.set_title("ADC Readings Over Time")
ax.set_xlabel("Time (s)")
for ch in adc_channels: ax.set_ylabel("Voltage (V)")
ax.plot(time_data, data[ch], label=f"ADC {ch+1}") ax.set_ylim(0, 12)
ax.legend(loc="upper right") for ch in adc_channels:
canvas.draw() ax.plot(time_data, data[ch], label=f"ADC {ch+1}")
frame.after(1000, update_plot)
ax.legend(loc="upper right")
update_plot() canvas.draw()
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()