How to detect button pressed and kept down and button up in Tkniter GUI?

Question:

I am trying to build a GUI tkiner and use it to control my Raspberry Pi robot.
The robot will have several functions but the main one will be to move forward, backward, left, and right using W, S, A, and D keys on the keyboard respectively, or by clicking the buttons on the GUI.
Here is my current code and screenshot of the GUI:

# Importing dependencies
import tkinter
import tkinter.messagebox
import customtkinter

# Setting up theme
customtkinter.set_appearance_mode("Dark")  # Modes: "System" (standard), "Dark", "Light"
customtkinter.set_default_color_theme("blue")  # Themes: "blue" (standard), "green", "dark-blue"


class App(customtkinter.CTk):
    def __init__(self):
        super().__init__()

        # configure window
        self.title("Cool Blue")
        self.geometry(f"{1200}x{700}")

        # configure grid layout (4x4)
        self.grid_columnconfigure(1, weight=1)
        self.grid_columnconfigure((2, 3), weight=0)
        self.grid_rowconfigure((0, 1, 2), weight=1)

        # create sidebar frame for controls
        self.sidebar_frame = customtkinter.CTkFrame(self, width=200)
        self.sidebar_frame.grid(row=0, column=0, rowspan=4, padx=(5, 5), pady=(10, 10), sticky="nsew")
        self.sidebar_frame.grid_rowconfigure(4, weight=1)
        self.logo_label = customtkinter.CTkLabel(self.sidebar_frame, text="Controls",  font=customtkinter.CTkFont(size=20, weight="bold"))
        self.logo_label.grid(row=0, column=1, padx=20, pady=(10, 10))
        self.button_up = customtkinter.CTkButton(self.sidebar_frame, text="W", height=10, width=10, command=self.motion_event)
        self.button_up.grid(row=1, column=1, padx=20, pady=10, ipadx=10, ipady=10)
        self.button_down = customtkinter.CTkButton(self.sidebar_frame, text="S", height=10, width=10, command=self.motion_event)
        self.button_down.grid(row=3, column=1, padx=20, pady=10, ipadx=10, ipady=10)
        self.button_left = customtkinter.CTkButton(self.sidebar_frame, text="A", height=10, width=10, command=self.motion_event)
        self.button_left.grid(row=2, column=0, padx=10, pady=10, ipadx=10, ipady=10)
        self.button_right = customtkinter.CTkButton(self.sidebar_frame, text="D", height=10, width=10, command=self.motion_event)
        self.button_right.grid(row=2, column=2, padx=10, pady=10, ipadx=10, ipady=10)
        self.button_stop = customtkinter.CTkButton(self.sidebar_frame, text="Stop", height=10, width=10, command=self.motion_event)
        self.button_stop.grid(row=2, column=1, padx=10, pady=10, ipadx=10, ipady=10)

        # create Video Canvas
        self.picam = customtkinter.CTkCanvas(self, width=800, background="gray")
        self.picam.grid(row=0, column=1, rowspan=2, padx=(5, 5), pady=(20, 20), sticky="nsew")
        self.picam_label = customtkinter.CTkLabel(master=self.picam, text="Live Video", font=customtkinter.CTkFont(size=20, weight="bold"))
        self.picam_label.grid(row=0, column=2, columnspan=1, padx=10, pady=10, sticky="")

        # create sidebar frame for Environmental Variable
        self.measurements = customtkinter.CTkFrame(self, width=200)
        self.measurements.grid(row=0, column=3, rowspan=4, padx=(5, 5), pady=(10, 10), sticky="nsew")
        self.measurements.grid_rowconfigure(4, weight=1)
        self.label_measurements = customtkinter.CTkLabel(master=self.measurements, text="Environment:", font=customtkinter.CTkFont(size=20, weight="bold"))
        self.label_measurements.grid(row=0, column=2, columnspan=1, padx=10, pady=10, sticky="")


    def motion_event(self):
        # if button_up pressed down or physical key "w" on keyboard pressed and held down, print "Forward" until key released or button not being clicked anymore.
        # else if button_down pressed down or physical key "s" on keyboard pressed and held down, print "Backward" until key released or button not being clicked anymore.
        # else if button_left pressed down or physical key "a" on keyboard pressed and held down, print "Left" until key released or button not being clicked anymore.
        # else if button_right pressed down or physical key "d" on keyboard pressed and held down, print "Right" until key released or button not being clicked anymore.


if __name__ == "__main__":
    app = App()
    app.mainloop()

enter image description here

So I created a basic layout with few buttons. What I am struggling with is the functionality of the buttons.
I need to implement fmotion_event function that will detect either if the key on my physical keyboard is pressed and held down or if the button on the GUI is being clicked and pressed. Once detected, to start I just need to get a printout of what key or button was clicked. Ultimately I want to replace this with the movement of the motors on the robot.

So for example, if the key "w" on the physical keyboard was pressed and held down or if the button "w" on GUI was pressed and held down, then print out "forward" and move my motors forward until the key or button was released.

Any suggestions on how do I accomplish this?
I understand you won’t be able to help me with moving motors forward but if you can help me by just printing out directions I can implement motor movements later.

Thank you in advance.

Asked By: Slavisha84

||

Answers:

You can bind all of these events to specific functions as callbacks. You can map one function for when the button is pressed and another for when it is released.

In the example below I have mapped each of the buttons to the mouse press and release buttons, each of them pass their respective character representations as parameters. Additionaly There are two bindings for keypress and keyrelease which also maps to two functions these functions check to see if the key pressed was an ‘A’, ‘S’, ‘D’, or ‘W’ and if it is then it passes them to the mouse move buttons since those two commands should be the same whether it is the mouse clicking the w or the keyboarding pressing the w. I left the stop button for you to do.

import tkinter
import tkinter.messagebox
import customtkinter

# Setting up theme
customtkinter.set_appearance_mode("Dark")  # Modes: "System" (standard), "Dark", "Light"
customtkinter.set_default_color_theme("blue")  # Themes: "blue" (standard), "green", "dark-blue"


class App(customtkinter.CTk):
    def __init__(self):
        super().__init__()

        # configure window
        self.title("Cool Blue")
        self.geometry(f"{1200}x{700}")

        # configure grid layout (4x4)
        self.grid_columnconfigure(1, weight=1)
        self.grid_columnconfigure((2, 3), weight=0)
        self.grid_rowconfigure((0, 1, 2), weight=1)
        self.bind("<KeyPress>", self.key_pressed)
        self.bind("<KeyRelease>", self.key_released)

        # create sidebar frame for controls
        self.sidebar_frame = customtkinter.CTkFrame(self, width=200)
        self.sidebar_frame.grid(row=0, column=0, rowspan=4, padx=(5, 5), pady=(10, 10), sticky="nsew")
        self.sidebar_frame.grid_rowconfigure(4, weight=1)

        self.logo_label = customtkinter.CTkLabel(self.sidebar_frame, text="Controls",  font=customtkinter.CTkFont(size=20, weight="bold"))
        self.logo_label.grid(row=0, column=1, padx=20, pady=(10, 10))

        self.button_up = customtkinter.CTkButton(self.sidebar_frame, text="W", height=10, width=10)
        self.button_up.grid(row=1, column=1, padx=20, pady=10, ipadx=10, ipady=10)
        self.button_up.bind('<ButtonPress-1>', lambda x: self.motion_event_start(x, 'W'))
        self.button_up.bind('<ButtonRelease-1>', lambda x: self.motion_event_stop(x, 'W'))

        self.button_down = customtkinter.CTkButton(self.sidebar_frame, text="S", height=10, width=10)
        self.button_down.grid(row=3, column=1, padx=20, pady=10, ipadx=10, ipady=10)
        self.button_down.bind('<ButtonPress-1>', lambda x: self.motion_event_start(x, 'S'))
        self.button_down.bind('<ButtonRelease-1>', lambda x: self.motion_event_stop(x, 'S'))

        self.button_left = customtkinter.CTkButton(self.sidebar_frame, text="A", height=10, width=10)
        self.button_left.grid(row=2, column=0, padx=10, pady=10, ipadx=10, ipady=10)
        self.button_left.bind('<ButtonPress-1>', lambda x: self.motion_event_start(x, 'A'))
        self.button_left.bind('<ButtonRelease-1>', lambda x: self.motion_event_stop(x, 'A'))

        self.button_right = customtkinter.CTkButton(self.sidebar_frame, text="D", height=10, width=10)
        self.button_right.grid(row=2, column=2, padx=10, pady=10, ipadx=10, ipady=10)
        self.button_right.bind('<ButtonPress-1>', lambda x: self.motion_event_start(x, 'D'))
        self.button_right.bind('<ButtonRelease-1>', lambda x: self.motion_event_stop(x,'D'))

        self.button_stop = customtkinter.CTkButton(self.sidebar_frame, text="Stop", height=10, width=10)
        self.button_stop.grid(row=2, column=1, padx=10, pady=10, ipadx=10, ipady=10)

        # create Video Canvas
        self.picam = customtkinter.CTkCanvas(self, width=800, background="gray")
        self.picam.grid(row=0, column=1, rowspan=2, padx=(5, 5), pady=(20, 20), sticky="nsew")
        self.picam_label = customtkinter.CTkLabel(master=self.picam, text="Live Video", font=customtkinter.CTkFont(size=20, weight="bold"))
        self.picam_label.grid(row=0, column=2, columnspan=1, padx=10, pady=10, sticky="")

        # create sidebar frame for Environmental Variable
        self.measurements = customtkinter.CTkFrame(self, width=200)
        self.measurements.grid(row=0, column=3, rowspan=4, padx=(5, 5), pady=(10, 10), sticky="nsew")
        self.measurements.grid_rowconfigure(4, weight=1)
        self.label_measurements = customtkinter.CTkLabel(master=self.measurements, text="Environment:", font=customtkinter.CTkFont(size=20, weight="bold"))
        self.label_measurements.grid(row=0, column=2, columnspan=1, padx=10, pady=10, sticky="")

    def key_pressed(self, event):
        if event.char and event.char in 'wads':
            self.motion_event_start(event, event.char.upper())
        elif event.keysym == 'Left':
            self.move_left_arm_function()
        elif event.keysym == 'Right':
            self.move_right_arm_function()

    def key_released(self, event):
        if event.char and event.char in 'wads':
            self.motion_event_stop(event, event.char.upper())
        elif event.keysym == 'Left':
             # do something
        elif event.keysym == 'Right':
             # something else

    def motion_event_start(self, event, button):
        print(f"{button} Pressed")

    def motion_event_stop(self, event, button):
        print(f"{button} Released")


if __name__ == "__main__":
    app = App()
    app.mainloop()
Answered By: Alexander
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.