I've got a piece of code similar to this:
def create_main(self):
self.bind("<Left>", lambda e:self.function())
self.button1 = Button(self, ...)
self.button1.grid(row=0, column =0)
#furtherbuttons...
def function(self):
print('test')
The Problem I've got with this code is that the function never gets called if I press left. I tried it with different buttons and the mouse, mouse buttons always work just fine but the keyboard does nothing.
I read something about buttons blocking the bind action, but no solution to this problem.
Here is an MCVE that works with 3.6.1 on Win10.
import tkinter as tk
root = tk.Tk()
def handle(event=None):
print(event)
return 'break'
root.bind('<Left>', handle)
tk.Button(root, text='button', command=handle)
root.mainloop()
Both <-- and left click print event arg. No interference or blocking.
Related
import tkinter
win=tkinter.Tk()
win.configure(background='grey')
k=False
def g():
k=True
v=tkinter.Button(win, text='click', command=g)
v.pack()
while k==True:
win.configure(background='black')
win.mainloop()
There's no reason why that while loop would run after the button is clicked, since (as you know) your program is run "from top to bottom", and control remains in win.mainloop() until the window is closed. (You can find that out by adding print("bye!") after that call.)
You might want to just directly call .configure(). (I gave the button some padding here so you can see the background change; otherwise the button may take up the entirety of the window and you won't see a change.)
import tkinter
win = tkinter.Tk()
def change_color():
win.configure(background='black')
button = tkinter.Button(win, text='click', command=change_color)
button.pack(padx=10, pady=10)
win.mainloop()
I know there are better ways about this, but I cant figure out what's
wrong about this code, or at least, why it wont function the way I want it. Currently I made a simple test program to try my concept away from my
main code.
from tkinter import *
root = Tk()
test = True
def click():
global test
print("working")
test = False
button = Button(root, text="Hi", command=click)
if test:
button.pack()
root.mainloop()
Everything runs fine but when I press the button all I get is the message "working" without the button going away.
In your code python checks if test is True and as it is, it packs the button and moves on. What you need to use is <tkinter widget>.pack_forget(). It removes the widget from the screen without destroying it. If you later call pack it should put it back in its original place. This is your code with the pack_forget:
from tkinter import *
root = Tk()
def click():
print("working")
button.pack_forget()
button = Button(root, text="Hi", command=click)
button.pack()
root.mainloop()
Let's say if we write something like this:
import threading
Button(root, command=threading.Thread(target=func1).start)
Now if we click the button once then it will be fine but we try to click the button again then a error comes "Thread can only be executed once".
So, how to avoid this
Edited Answer::
As you clarified it in comments, you can redefine the Button every time it is clicked allowing it to accept multiple clicks and thus creating multiple threads as required.
You could do that inside the target func1() or callback function for threading.Thread object.
A working example example would be like this:
import tkinter as tk
import threading
def func1():
theButton.configure(command=threading.Thread(target=func1).start)
print('Do everything else here')
root = tk.Tk()
theButton = tk.Button(root, text='Start', command=threading.Thread(target=func1).start)
theButton.pack()
root.mainloop()
Edit: Thanks to CoolCloud for suggesting a better way to configure the Button inside callback func1().
It is because you create one instance of threading.Thread() and pass its start method to command option of the button. You should create new instance whenever the button is clicked by using lambda:
import tkinter as tk
import threading
root = tk.Tk()
def func1():
print('Hello')
tk.Button(root, text='Go', command=lambda: threading.Thread(target=func1).start()).pack()
root.mainloop()
I am trying to build a program that listens for certain key combinations and then shows information to the user in a Tkinter window. To do this, I'm using a keylogger like so (simplified for this example):
from pyHook import HookManager
from pythoncom import PumpMessages
import Tkinter as tk
def on_keyboard_event(event):
label.config(text=event.Key)
root.update()
return True
hm = HookManager()
hm.KeyDown = on_keyboard_event
hm.HookKeyboard()
root = tk.Tk()
label = tk.Label(root, text='Hello world')
label.pack()
PumpMessages()
As expected, the window pops up and shows the user what key they pressed. However, I would like to integrate functionality to show other messages by interacting with the Tkinter window, such as by pressing a button. However, it seems I need Tkinter's mainloop to do this, which I can't figure out how to run alongside PumpMessages(), since it also halts the code similar to mainloop().
I tried running root.mainloop() in a root.after(), and I tried recreating root.mainloop like so:
def mainloop():
root.update()
root.after(50, mainloop)
and then running it right before PumpMessages, but neither of these solutions worked. It also doesn't seem like you can run PumpMessages or root.mainloop in a thread, though I could just not be doing it right. If this is not possible with Tkinter, is there an alternate Python GUI I could use that would make it possible?
You don't need to create a function to use mainloop() so just simply place the mainloop() at the bottom of your code. If you want a delay on it, use root.after(milliseconds, function)
Also, remember to put mainloop() before PumpMessages()
e.g.
def mainloopfunction():
mainloop()
root.after(5000, mainloopfunction)
Hope I could help!
Trying this again...I have a Python programmed GUI in which the pressed Button retains a depressed look after the event handler exits. The event handler made use of a messagebox. Normally, this does not happen. Here is an example that recreates the problem:
import tkinter as tk
from tkinter import messagebox
# post a message
def post_message(event):
messagebox.showinfo("Sample Messgebox", "close this and look at button")
root = tk.Tk()
b = tk.Button(root, text="Press Me")
b.bind("<Button-1>", func=post_message)
b.pack()
root.mainloop()
When you use the blind with the event Button-1, you aren't using the main event of the button. You can active the main event of the button with the argument command.
import tkinter as tk
from tkinter import messagebox
def post_message():
messagebox.showinfo("Sample Messgebox", "close this and look at button")
root = tk.Tk()
b = tk.Button(root, text="Press Me", command=post_message)
b.pack()
root.mainloop()
While I'm not sure why your code isn't working properly, since I'm fairly new to Py, I managed to rewrite it to work with minimal changes.
Solution 1
import tkinter as tk
from tkinter import messagebox
# post a message
def post_message():
messagebox.showinfo("Sample Messgebox", "close this and look at button")
root = tk.Tk()
b = tk.Button(root, text="Press Me", command=post_message)
b.pack()
root.mainloop()
What I changed:
no more bind() as this caused problem, instead the function is called by adding command= option while declaring Button object,
also notice that command option doesn't provide function called with event parameter, so this had to be removed or else errors would occur.
Another workaround, this time it works with bind() just fine!
Solution 2
import tkinter as tk
from tkinter import messagebox
# post a message
def post_message(event):
root.after(0, lambda: messagebox.showinfo\
("Sample Messgebox", "close this and look at button"))
root = tk.Tk()
b = tk.Button(root, text="Press Me")
b.bind("<Button-1>", post_message)
b.pack()
root.mainloop()
I used master.after(time_in_ms, callback_func) to tell the program that it should run a given func after the given time, here 0ms so ASAP.
Why is that lambda inside after? Lambda is a dynamic, not-named function. After takes a reference to the function you want called, so you can't directly give it parameters.
To do so, like in this example, set up a lambda that will be refrenced.
When it finally gets called, that lambda func will then call the actual function you wanted to call giving it the parameters it needs.
If you don't know yet how lambdas work, I know you're confused right now, so read more on them here, they're super-useful: Lambdas explained
For great source of info on tkinter, please visit effbot.org Events and Bindings