Quantcast
Viewing latest article 4
Browse Latest Browse All 1225

Python • Re: How to use background images to change buttons status in tkinter GUI?

And here are my 3 created GUI images (800 x 480px) showing the different buttons status:
GUI with button in the default status:
Image may be NSFW.
Clik here to view.
GUI_OFF.png

GUI with the buttons in "Clicked status":
Image may be NSFW.
Clik here to view.
GUI_Clicked.png

and the third image showing the buttons in activated/ON status:
Image may be NSFW.
Clik here to view.
GUI_ON.png
all my button are 60px x 60px and are in this grid coordinate (X , Y):

Botton1 (72, 20)
Botton2 (336, 20)
Botton3 (420, 20)
Botton4 (580, 20)
Botton5 (72, 89)
Botton6 (336, 89)
Botton7 (420, 89)
Botton8 (580, 89)
Botton9 (74, 158)
Botton10 (245, 226)
Botton11 (420, 155)
Botton12 (580, 155)
Botton13 (74, 222)
Botton14 (370, 420)
Botton15 (420, 222)
Botton16 (580, 222)

Any idea how I can modify my code below to have the buttons on the GUI change its image from its 60 x 60px area to the images of the background images depending on the button status?

Here is my current code for the tkinter GUI with conventional buttons:

Code:

import tkinter as tkimport RPi.GPIO as GPIOimport threadingimport timefrom datetime import datetimeimport queuefrom apscheduler.schedulers.background import BackgroundSchedulerimport loggingimport adafruit_dhtimport boardimport subprocess# Variable to keep track of the music processmusic_process = None# Function to play or stop musicdef toggle_music():    global music_process        # Path to the audio file you want to play    audio_file = "/home/microgreen/Music/music.mp3"  # Replace with your actual file path        if music_process is None or music_process.poll() is not None:        # Music is not playing, so start it with looping in the background        music_process = subprocess.Popen(            ["vlc", "--intf", "dummy", "--loop", audio_file],            stdout=subprocess.PIPE,            stderr=subprocess.PIPE        )        play_button.config(text="Stop", bg="red")  # Change button text to "Stop Music"    else:        # Music is playing, so stop it        music_process.terminate()  # Terminate the VLC process        music_process = None        play_button.config(text="Play", bg="green")  # Change button text back to "Play Music"# Enable detailed logginglogging.basicConfig(level=logging.DEBUG, format='%(message)s')# GPIO SetupGPIO.setmode(GPIO.BCM)GPIO.setwarnings(False)# DHT22 Sensor SetupDHT_SENSOR = adafruit_dht.DHT22(board.D4)  # Replace with actual GPIO pin# Define GPIO pinspins = {    "LED Shelf #1 & 2": 17,    "LED Shelf #3 & 4": 18,    "Water Sprayer": 25,    "Fans": 5,    "Water Shelf 1": 27,    "Water Shelf 2": 22,    "Water Shelf 3": 23,    "Water Shelf 4": 24}# Set up GPIO as outputfor pin in pins.values():    GPIO.setup(pin, GPIO.OUT)    GPIO.output(pin, GPIO.HIGH)  # OFF state for relays# Initialize GUIwin = tk.Tk()win.title("Microgreens Farm Controller")win.geometry("800x480")# Default colors for OFF stateDEFAULT_COLORS = {    17: "#ffeb3b",    18: "#ffeb3b",    5: "#FFA500",    25: "#87CEEB",}# Checkbox control variablesshelf_enabled_vars = {pins[f"Water Shelf {i}"]: tk.IntVar() for i in range(1, 5)}# Timer valuestimer_values = {pins[f"Water Shelf {i}"]: tk.StringVar() for i in range(1, 5)}def start_timer(pin, button, duration, label):    time.sleep(duration)    GPIO.output(pin, GPIO.HIGH)    button.config(text=f"{label} OFF", bg="#87CEFA")def toggle_water_shelf(pin, button, label):    print(f"Toggling water shelf {label} with pin {pin}")    if shelf_enabled_vars[pin].get():        if GPIO.input(pin):            GPIO.output(pin, GPIO.LOW)            button.config(text=f"{label} ON", bg="#2ecc71")            print(f"Water shelf {label} turned ON")            try:                duration = int(timer_values[pin].get())            except ValueError:                duration = 10            threading.Thread(target=start_timer, args=(pin, button, duration, label), daemon=True).start()            logging.debug(f"Water Shelf {label} turned ON for {duration} seconds.")        else:            GPIO.output(pin, GPIO.HIGH)            button.config(text=f"{label} OFF", bg="#87CEFA")            logging.debug(f"Water Shelf {label} turned OFF.")    else:        logging.debug(f"Watering not enabled for {label}.")# Scheduler variablesday_vars = {day: tk.IntVar() for day in ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]}hour_vars = {hour: tk.IntVar() for hour in range(24)}schedule_shelf_vars = {pins[f"Water Shelf {i}"]: tk.IntVar() for i in range(1, 5)}# Light cycle settingsled_on_durations = {pin: tk.StringVar(value="16") for pin in [17, 18]}led_off_durations = {pin: tk.StringVar(value="8") for pin in [17, 18]}# Scheduler setupscheduler = BackgroundScheduler()scheduler.start()def apply_led_schedule(pin, button, label):    """Applies the light schedule for the given LED."""    try:        on_duration = int(led_on_durations[pin].get())        off_duration = int(led_off_durations[pin].get())    except ValueError:        logging.error("Invalid duration input. Using default 12 ON / 6 OFF.")        on_duration, off_duration = 12, 6    def turn_on():        if GPIO.input(pin) == 0:  # Only turn ON if it was previously ON            GPIO.output(pin, GPIO.LOW)            button.config(text=f"{label} ON", bg="#2ecc71")            logging.info(f"{label} turned ON")            scheduler.add_job(turn_off, 'interval', hours=on_duration, id=f"led_off_{pin}")        def turn_off():        GPIO.output(pin, GPIO.HIGH)        button.config(text=f"{label} OFF", bg=DEFAULT_COLORS[pin])        logging.info(f"{label} turned OFF")        scheduler.add_job(turn_on, 'interval', hours=off_duration, id=f"led_on_{pin}")        turn_on()# Temperature & Humidity Sensor Frametemp_frame = tk.Frame(win)temp_frame.pack(side="top", pady=0)temp_label = tk.Label(temp_frame, text="Temp: --", font=("Arial", 12))temp_label.pack(side="left", padx=5)humidity_label = tk.Label(temp_frame, text="Humidity: --", font=("Arial", 12))humidity_label.pack(side="right", padx=5)"""Reads temperature and humidity from DHT22 and updates the GUI."""def update_temp_humidity():    try:        # Read values from the DHT22 sensor        temperature = DHT_SENSOR.temperature        humidity = DHT_SENSOR.humidity    except RuntimeError as error:        logging.error(f"Sensor error: {error}")        temperature, humidity = None, None    if humidity is not None and temperature is not None:        # Update the labels with the sensor readings        temp_label.config(text=f"Temp: {temperature:.1f}\u00B0C")        humidity_label.config(text=f"Humidity: {humidity:.1f}%")    else:        # If there is an error with the sensor, display "--"        temp_label.config(text="Temp: --")        humidity_label.config(text="Humidity: --")        # Schedule the next update in 5 seconds    win.after(5000, update_temp_humidity)# Start the first update when the program startsupdate_temp_humidity()def toggle_device(pin, button, label):    """Manually toggles LED state and cancels the schedule if turned OFF manually."""    if GPIO.input(pin):        GPIO.output(pin, GPIO.LOW)        button.config(text=f"{label} ON", bg="#2ecc71")    else:        GPIO.output(pin, GPIO.HIGH)        button.config(text=f"{label} OFF", bg=DEFAULT_COLORS.get(pin, "#ffeb3b"))        scheduler.remove_job(f"led_off_{pin}", jobstore=None)        scheduler.remove_job(f"led_on_{pin}", jobstore=None)# Main layoutframe = tk.Frame(win)frame.pack(expand=True, fill="both", pady=1)left_frame = tk.Frame(frame)left_frame.grid(row=0, column=0, padx=12, sticky="n")LED_frame = tk.Frame(frame)LED_frame.grid(row=0, column=1, padx=0, pady=15, sticky="n")water_shelf_frame = tk.Frame(frame)water_shelf_frame.grid(row=0, column=2, padx=5, sticky="n")# Create a button to toggle music playbackplay_button = tk.Button(left_frame, text="Play", command=toggle_music, width=2, height=0, bg="green")play_button.grid(row=2, column=3, padx=0, pady=10, sticky="n")def create_toggle_button(label, pin, row):    button = tk.Button(left_frame, text=label + " OFF", bg=DEFAULT_COLORS.get(pin, "#ffeb3b"), width=16, height=2)    button.config(command=lambda: toggle_device(pin, button, label))    button.grid(row=row, column=0, padx=2, pady=2, sticky="w")    return buttoncontrols = [    ("LED Shelf #1 & 2", 17),    ("LED Shelf #3 & 4", 18),    ("Water Sprayer", 25),    ("Fans", 5),]for i, (label, pin) in enumerate(controls):    create_toggle_button(label, pin, i)def create_water_shelf_button(label, pin, row):    button = tk.Button(water_shelf_frame, text=label + " OFF", bg="#87CEFA", width=16, height=2)    button.config(command=lambda: toggle_water_shelf(pin, button, label))    button.grid(row=row, column=0, padx=2, pady=2, sticky="w")        shelf_checkbox = tk.Checkbutton(water_shelf_frame, text="Enable Watering", variable=shelf_enabled_vars[pin])    shelf_checkbox.grid(row=row, column=1, padx=2, pady=5, sticky="w")        timer_entry = tk.Entry(water_shelf_frame, textvariable=timer_values[pin], width=5)    timer_entry.grid(row=row, column=2, padx=2, pady=5, sticky="e")    timer_values[pin].set("10")    timer_label = tk.Label(water_shelf_frame, text="Seconds")    timer_label.grid(row=row, column=3, padx=2, pady=2, sticky="e")    return buttonwater_shelf_buttons = {}for i in range(1, 5):    water_shelf_buttons[pins[f"Water Shelf {i}"]] = create_water_shelf_button(f"Water Shelf {i}", pins[f"Water Shelf {i}"], i)# Scheduler Framescheduler_frame = tk.Frame(win)scheduler_frame.pack(side="top", pady=0)# Scheduler (Shelves Section)shelves_frame = tk.Frame(win)shelves_frame.pack(side="top", pady=0)# Scheduler (Days Section)days_frame = tk.Frame(win)days_frame.pack(side="top", pady=0)# Scheduler (Hours Section)hours_frame = tk.Frame(win)hours_frame.pack(side="top", pady=0)# Scheduler (Set Schedule Button)setButton_frame = tk.Frame(win)setButton_frame.pack(side="top", pady=0)# Function to toggle devicesdef toggle_device(pin, button, label):    if GPIO.input(pin):        GPIO.output(pin, GPIO.LOW)        button.config(text=f"{label} ON", bg="#2ecc71")    else:        GPIO.output(pin, GPIO.HIGH)        button.config(text=f"{label} OFF", bg=DEFAULT_COLORS.get(pin, "#ffeb3b"))                def schedule_watering():    selected_days = [day for day, var in day_vars.items() if var.get() == 1]    selected_hours = [hour for hour, var in hour_vars.items() if var.get() == 1]    selected_shelves = [shelf for shelf, var in schedule_shelf_vars.items() if var.get() == 1]    # Create popup window to confirm schedule    schedule_popup = tk.Toplevel(win, padx=20, pady=10)    schedule_popup.title("Schedule Confirmation")    tk.Label(schedule_popup, text="Scheduled Confirmation", font=("Arial", 14), padx=30, pady=5).pack()    tk.Label(    schedule_popup,     text=f"Scheduled: {', '.join([f'Water Shelf {list(pins.keys())[list(pins.values()).index(s)].split()[-1]} ({timer_values[s].get()}s)' for s in selected_shelves])}").pack()    tk.Label(schedule_popup, text=f"Scheduled Days: {', '.join(selected_days)}").pack()    tk.Label(schedule_popup, text=f"Scheduled Hours: {', '.join(map(str, selected_hours))}").pack()    for day in selected_days:        for hour in selected_hours:            for shelf in selected_shelves:                shelf_label = f"Water Shelf {list(pins.values()).index(shelf)}"  # Get correct label                                scheduler.add_job(                    lambda s=shelf, l=shelf_label: toggle_water_shelf(s, water_shelf_buttons[s], l),                    'cron', day_of_week=day.lower(), hour=hour, minute=0                )tk.Label(scheduler_frame, text="Watering Schedule", font=("Arial", 12, "bold")).grid(row=0, column=0, columnspan=8, sticky="n")# Days selectiontk.Label(days_frame, text="Days:").grid(row=1, column=0, sticky="n")for i, (day, var) in enumerate(day_vars.items()):    tk.Checkbutton(days_frame, text=day, variable=var).grid(row=1, column=i+1, sticky="n")# Hours selection with AM/PM labelstk.Label(hours_frame, text="Hours:").grid(row=2, column=0, sticky="n")hour_vars = {hour: tk.IntVar() for hour in range(24)}for i, (hour, var) in enumerate(hour_vars.items()):    am_pm_label = f"{hour % 12 if hour % 12 else 12}{'am' if hour < 12 else 'pm'}"    tk.Checkbutton(hours_frame, text=am_pm_label, variable=var).grid(row=2 + (i // 12), column=(i % 12) + 1, sticky="n")# Shelves selectiontk.Label(shelves_frame, text="Shelves:").grid(row=3, column=0, sticky="w")for i, (shelf, var) in enumerate(schedule_shelf_vars.items()):    tk.Checkbutton(shelves_frame, text=f"Shelf {i+1}", variable=var).grid(row=3, column=i+1, sticky="n")# Schedule Buttontk.Button(setButton_frame, text="Set Schedule", bg="green", fg="white", command=schedule_watering).grid(row=1, column=2, columnspan=8, pady=0)def enqueue_event(shelf):    logging.debug(f"Enqueueing event for {shelf}")    print(f"Watering {shelf} for the scheduled duration.")        # LED Controlsfor i, (label, pin) in enumerate([("LED Shelf #1 & 2", 17), ("LED Shelf #3 & 4", 18)]):    button = tk.Button(left_frame, text=label + " OFF", bg=DEFAULT_COLORS[pin], width=16, height=2)    button.config(command=lambda p=pin, b=button, l=label: toggle_device(p, b, l))    button.grid(row=i, column=0, padx=2, pady=2, sticky="w")        tk.Label(left_frame, text="ON").grid(row=i, column=1)    tk.Entry(left_frame, textvariable=led_on_durations[pin], width=2).grid(row=i, column=2)        tk.Label(left_frame, text="OFF").grid(row=i, column=3)    tk.Entry(left_frame, textvariable=led_off_durations[pin], width=2).grid(row=i, column=4)    tk.Label(left_frame, text="Hrs").grid(row=i, column=5, pady=13)        tk.Button(left_frame, text="Set", command=lambda p=pin, b=button, l=label: apply_led_schedule(p, b, l)).grid(row=i, column=6, padx=1)# Exit program functiondef stopProgram():    scheduler.shutdown()    GPIO.cleanup()    win.quit()tk.Button(win, text="Exit", bg="#c0392b", fg="white", width=15, height=1, command=stopProgram).pack(side="bottom", pady=2)win.mainloop()
Any help with this would be appreciated...

Statistics: Posted by papagino — Fri Apr 04, 2025 9:09 pm



Viewing latest article 4
Browse Latest Browse All 1225

Trending Articles