How to bind a keypress to a button in Tkinter - python

My Senior Project involves a robot I can control over wifi. I am using a Raspberry Pi and a Tkinter window to send commands to the robot. I have the rough draft of my Tkinter window, but I am wondering if there is a way to bind the button press to the arrow keys. That way I can control the robot by using my arrow keys rather than clicking on each button. Here is my code, what would I have to add?
Code:
from Tkinter import *
message = ""
class App:
def __init__(self, master):
frame=Frame(master)
frame.grid()
status = Label(master, text=message)
status.grid(row = 0, column = 0)
self.leftButton = Button(frame, text="<", command=self.leftTurn)
self.leftButton.grid(row = 1, column = 1)
self.rightButton = Button(frame, text=">", command=self.rightTurn)
self.rightButton.grid(row = 1, column = 3)
self.upButton = Button(frame, text="^", command=self.upTurn)
self.upButton.grid(row = 0, column = 2)
self.downButton = Button(frame, text="V", command=self.downTurn)
self.downButton.grid(row=2, column = 2)
def leftTurn(self):
message = "Left"
print message
def rightTurn(self):
message = "Right"
print message
def upTurn(self):
message = "Up"
print message
def downTurn(self):
message = "Down"
print message
root = Tk()
root.geometry("640x480")
root.title("Rover ")
app = App(root)
root.mainloop()

I believe what you want is to bind the key press to the frame/function. Tkinter has its own event and binding handling built in which you can read up on here.
Here is quick example which you should be able to adapt your program.
from tkinter import *
root = Tk()
def yourFunction(event):
print('left')
frame = Frame(root, width=100, height=100)
frame.bind("<Left>",yourFunction) #Binds the "left" key to the frame and exexutes yourFunction if "left" key was pressed
frame.pack()
root.mainloop()

Related

Python tkinter: why does he want to quit?

I would need a bit of help with my code I'm writing... and although when I start the code then comes directly such a window which asks me if I want to close it.
Does anyone have an idea where this could come from?
The code should open a window where I have some buttons to open some other windows with sliders to control DMX Lights.
Here is the code:
import tkinter as tk
from tkinter import ttk
from tkinter import *
from print_dict import pd
from time import sleep
from tkinter import messagebox
Pultdatas = {'DMXtype':512,
'columns':2,
'rows':8,
'slider':8,
'modes':4
}
root = Tk()
root.configure(background='gray')
root.title('DMX-Pult v2')
Screens = {'mainscreen':[root,{}]}
def on_closing(screen):
if messagebox.askokcancel("Quit", f"Do you want to quit Scanner {screen}?"):
Screens[screen][0].destroy()
def setmode(screen,mode):
print(f'setmode on screen {screen} to {mode}')
for i in Screens[screen][1]['modesel']:
print(i)
i.config(bg='gray')
Screens[screen][1]['modesel'][mode].config(bg='green')
def pressed(btn):
print('pressed Button: ' + str(btn))
if not Screens.__contains__(btn):
Screens[btn] = []
Screens[btn].append(Tk())
Screens[btn][0].configure(background='gray')
Screens[btn][0].title(f'Scanner {btn}')
Screens[btn].append({})
Screens[btn][1]['modesel'] = []
for i in range(Pultdatas['modes']):
Screens[btn][1]['modesel'].append(Button(Screens[btn][0], text=f"mode {i+1}", bg='gray', fg='white', command=lambda name = i:setmode(btn,name)))
Screens[btn][1]['modesel'][i].grid(row=i,column=0)
setmode(btn,0)
else:
if Screens[btn][0].winfo_exists() == 1:
sleep(0.2)
print('This window exist.')
Screens[btn][0].attributes('-topmost', True)
Screens[btn][0].update()
Screens[btn][0].attributes('-topmost', False)
else:
Screens[btn] = []
Screens[btn].append(Tk())
Screens[btn][0].configure(background='gray')
Screens[btn][0].title(f'Scanner {btn}')
Screens[btn].append({})
Screens[btn][1]['modesel'] = []
for i in range(Pultdatas['modes']):
Screens[btn][1]['modesel'].append(Button(Screens[btn][0], text=f"mode {i + 1}", bg='gray', fg='white',
command=lambda name=i: setmode(btn, name)))
Screens[btn][1]['modesel'][i].grid(row=i, column=0)
Screens[btn][0].protocol("WM_DELETE_WINDOW", lambda name = btn:on_closing(name))
setmode(btn, 0)
print()
def close():
if messagebox.askokcancel("Quit", f"Do you want to quit?"):
for screen in Screens:
print(f'closed {Screens[screen][0].title()} Succesfully')
Screens[screen][0].destroy()
tmpint = 0
Screens['mainscreen'][1]['Back'] = Button(root, text='close all',bg='gray',fg='white',command=close)
Screens['mainscreen'][1]['Back'].grid(row=0,column=0)
for column in range(Pultdatas['columns']):
for row in range(Pultdatas['rows']):
tmpint += 1
Screens['mainscreen'][1][tmpint] = Button(root, text=f"Scanner {tmpint}", bg='gray', fg='white', command=lambda name = tmpint:pressed(name))
Screens['mainscreen'][1][tmpint].grid(row=row+1,column=column)
pd(Screens)
root.protocol("WM_DELETE_WINDOW", close())
root.mainloop()
You can just change this line of code
root.protocol("WM_DELETE_WINDOW", close())
To this:
root.protocol("WM_DELETE_WINDOW", close)
You don't need to write (), we can just write handler not as function.
For more info you can visit here

Clear tkinter label contents after set time

I have a label that is updated with status information. What I would like to do is clear its contents after 10 seconds if the status doesn't change. For example I set the label to to "step one" then after 10 seconds it gets cleared "". Now if before the 10 seconds expires I set the label to "Step two" it will update and reset the the 10 second timer.
EDIT:
OK, I never could get this to work right. I made a small app to test that has 3 buttons, two of which just change the text of a label. What I want to happen is the following.
Update the label with the text (Push button 1, or 2)
start a 2 second timer. (after 2 seconds clear the label)
if button 1 or 2 is pressed before 2 seconds is up update label and reset the timer.
what I'm seeing is the timer is holding up things. If I press button 1 the label is updated and the timer starts. But I can't press either buttons again until the timer is finished
import tkinter as tk
class Window(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.master = master
self.myafter = None
# widget can take all window
self.pack(fill= tk.BOTH, expand=1)
# create button, link it to clickExitButton()
startButton = tk.Button(self, text="MSG_1", command=self.clickButton1 )
startButton.place(x=10,y=10,width=50,height=50)
stopButton = tk.Button(self, text="MSG_2", command=self.clickButton2)
stopButton.place(x=100,y=10,width=50,height=50)
exitButton = tk.Button(self, text="EXIT", command=self.clickExitButton)
exitButton.place(x=190,y=10,width=50,height=50)
lblstatus = tk.Label(self, text = "Status:")
lblstatus.place(x=10, y=150)
self.lblStatus = tk.Label(self, text = "READY")
self.lblStatus.place(x=75,y=150)
def clickButton1(self):
if(self.myafter != None):
self.lblStatus.after_cancel(self.myafter)
app.UpdateMessage("Set message 1")
def clickButton2(self):
if(self.myafter != None):
self.lblStatus.after_cancel(self.myafter)
app.UpdateMessage("Set message 2")
def ClearStatus(self):
self.lblStatus["text"] = ""
def UpdateMessage(self,msg= "test"):
self.lblStatus["text"] = msg
self.lblStatus.update()
if(self.myafter == None):
self.myafter = self.lblStatus.after(2000,self.ClearStatus())
def clickExitButton(self):
root.quit()
root = tk.Tk()
app = Window(root)
root.wm_title("Label Clear Test")
root.geometry("300x200")
root.mainloop()
Thanks
PBSnake
Use the threading module for this.
threading.Timer is an object that will run parallel to your GUI so it
won't freeze it.
This code below should do the trick:
import tkinter as tk
import threading
def myLoop():
var.set(var.get() + 1) #Increment the value
threading.Timer(1, myLoop).start()
window = tk.Tk()
var = tk.IntVar()
var.set(1)
button = tk.Button(window, text = 'Threading', command = lambda: threading.Timer(1, myLoop).start())
button.pack()
label = tk.Label(window, textvariable = var)
label.pack()
window.mainloop()

How to use ttk combobox with keyboard entry using return key press as event

I am trying to learn how to use ttk Combo box and got as far as setting it up, populating, getting it to respond to selection of a listed item using a button control and alternatively binding to the ComboboxSelected event. The latter works for listed items coded in but I am missing something to make it work for an entry typed into the Combo box. I can type in an item and it will respond if I click a button but typing in an item and pressing enter does not work. I am trying to get the combo box fully functional without a button.
import tkinter as tk
from tkinter import ttk
window = tk.Tk()
window.minsize(600, 400)
window.title("ttk Combo box")
def chooseNumbers():
label.configure(text = "You Selected " + mynumber.get())
def callbackfn(event):
label.configure(text = "You Have Selected " + mynumber.get())
label = ttk.Label(window, text = "Choose A Number")
label.grid(column = 0, row = 0)
mynumber = tk.StringVar()
combobox = ttk.Combobox(window, width = 15 , textvariable = mynumber,state='normal')
combobox['values'] = (5,6,7,8,9,10,12,14,15)
combobox.grid(column = 0, row = 1)
button = ttk.Button(window, text = "Click Me", command = chooseNumbers) # don't want this
button.grid(column = 1, row = 1)
combobox.bind("<<ComboboxSelected>>",callbackfn) # how to get this to work with keyboard entry?
window.mainloop()
Is this what you want?
import tkinter as tk
from tkinter import ttk
window = tk.Tk()
window.minsize(600, 400)
window.title("ttk Combo box")
def chooseNumbers(event):
label.configure(text = "You Selected " + mynumber.get())
def callbackfn(event):
label.configure(text = "You Have Selected " + mynumber.get())
label = ttk.Label(window, text = "Choose A Number")
label.grid(column = 0, row = 0)
mynumber = tk.StringVar()
combobox = ttk.Combobox(window, width = 15 , textvariable = mynumber,state='normal')
combobox['values'] = (5,6,7,8,9,10,12,14,15)
combobox.grid(column = 0, row = 1)
combobox.bind("<<ComboboxSelected>>",callbackfn) # how to get this to work with keyboard entry?
combobox.bind("<Return>",chooseNumbers)
window.mainloop()
When you press button Enter,It will show "You Selected x".Just bind a event <Return> and call a function.

How do i check the state of bind() function in tkinter? (Python 3.8)

The goal of the program was to create a never-ending game of clicking the button. Every time the mouse pointer hovered over the button btn, it would move randomly across the window. But I can't seem to get the status of when the mouse hovers over the button. pardon any mistakes and feel free to correct them, I'm just a beginner. Heres the code -
from tkinter import *
import time
import random
window = Tk()
window.geometry("500x500")
lbl = Label(window, text = "Try to click the button")
lbl.pack()
def move_btn():
global btn
if btn.bind("<Enter>") == True:
wi = random.randint(0, 500)
hi = random.randint(0, 500)
btn.place(x = hi, y = wi)
btn = Button(window, text = "Click ME!",command = move_btn)
btn.pack()
window.mainloop()

Interrupting Loops in a tkinter GUI via a button.

I'm coding a timer app, which has encountered a lot of difficulties, but one of the first ones has been in making a loop that can be broken by the press of a button. I've looked into it, and my research shows I should use threading, but I couldn't figure out how it would work.
What I decided to try, then, was to make an exception when I invoked a keyboard interrupt, and then make a button that calls that same interrupt. However, my current code refuses to interrupt when I ctrl-c.
My sample code looks like this
from Tkinter import *
from sys import exit
class Timer:
def __init__(self, master):
buttonstart = Button(master, text = "Start", fg = "blue", command = self.start)
buttonstart.grid(row = 1, column = 0)
buttonquit = Button(master, text = "Quit", fg = "blue", command= quit)
buttonquit.grid(row = 1, column = 2)
global timertext
timertext = DoubleVar()
timertext.set(0)
display = Label(master, textvariable = timertext)
display.grid(row = 0, column = 0)
timertext.set(timertext)
def timerlogic(self):
pass
def pause(self):
pass
def start(self):
global timertext
try:
while True:
#do things
except KeyboardInterrupt:
print "Interrupted"
def lap(self):
pass
root = Tk()
app = Timer(root)
root.mainloop()
root.destroy()
Basically, I don't think my code as it stands is viable, but I don't know how to edit it to make a loop I can interrupt as needed.
You set some variable to True or False. Also, a while loop interrupts the Tkinter loop so Tkinter will do nothing until the while loop exits. Use Tkinter's after function instead.
from Tkinter import *
##from sys import exit
class Timer:
def __init__(self, master):
self.master=master
buttonstart = Button(master, text = "Start", fg = "blue", command = self.start)
buttonstart.grid(row = 1, column = 0)
buttonquit = Button(master, text = "Quit", fg = "blue", command=self.quitit)
buttonquit.grid(row = 1, column = 2)
self.timertext = DoubleVar()
self.timertext.set(0)
display = Label(master, textvariable = self.timertext)
display.grid(row = 0, column = 0)
## timertext.set(timertext) ## Huh!!
self.timeit=False
def increment_timer(self):
ctr=int(self.timertext.get())
self.timertext.set(ctr+1)
if self.timeit:
self.master.after(500, self.increment_timer)
def start(self):
self.timeit=True
self.increment_timer()
def quitit(self):
self.timeit=False
root = Tk()
app = Timer(root)
root.mainloop()

Categories

Resources