Query on Python tkinter bind event - python

Friends, I'm trying to make hover effect in python(3.5.2) on a button, I use the below code.
from tkinter import *
root = Tk()
root.geometry("200x200+400+400")
myBtn = Button(root, text="TEST")
myBtn.pack(padx=10, pady=10)
myBtn.bind("<Enter>", lambda event, h=myBtn: h.configure(bg="red"))
myBtn.bind("<Leave>", lambda event, h=myBtn: h.configure(bg="yellow"))
root.mainloop()
It works strange.
when I hover mouse over the button, first time nothing happens
and when I leave, it gives yellow (satisfying the second event binded).
I couldn't find where I made mistake. could some one point me out

In your <Enter> binding you just need to set activebackground instead of the plain background (bg is a synonym of background).
import tkinter as tk
root = tk.Tk()
root.geometry("200x200+400+400")
myBtn = tk.Button(root, text="TEST")
myBtn.pack(padx=10, pady=10)
myBtn.bind("<Enter>", lambda event, h=myBtn: h.configure(activebackground="red"))
myBtn.bind("<Leave>", lambda event, h=myBtn: h.configure(bg="yellow"))
root.mainloop()
I changed the import statement from the messy "star" import, which imports 135 Tkinter names into your namespace. This requires a little more typing, but it makes the code easier to read & maintain because it makes it obvious which names come from Tkinter.

Related

Customizing Button Commands in Python Tkinter

I've recently started learning how to use Tkinter. I'm working on the front-end for a small project to design an Enigma Machine, and a part of it involves creating a keyboard-like layout of buttons to simulate the plugboard. When clicking one button, I want it to go to a method I've created to handle all button presses and tell the method which letter the button corresponds to, and act accordingly. The main task right now is to make it so that pressing one button and then pressing another button after that draws a line between both buttons.
What I've added below is a scuffed version of the actual program to cut out repeated code like the button declarations.
from tkinter import *
from tkinter import ttk
from PIL import ImageTk, Image
class Steckerbrett:
root = Tk()
frm = ttk.Frame(root, padding=10)
frm.grid(row=0, column=0)
ttk.Label(frm, text="Enigma Machine").grid(column=0, row=0)
ttk.Label(frm, text="Steckerbrett").grid(column=0, row=1)
ttk.Label(frm, text="").grid(column=0, row=2)
# plugboard keys row 1
frm1 = ttk.Frame(root, padding=10)
frm1.grid(row=1, column=0)
ttk.Button(frm1, text="Q", command=connection).grid(column=0, row=0)
ttk.Button(frm1, text="W", command=connection).grid(column=1, row=0)
# i have declarations like this for all 26 letters
root.mainloop()
def connection(self, letter):
ttk.Label(self.frm, text=letter).grid(column=0, row=2)
sb = Steckerbrett()
The issue here is that since 'command' doesn't allow entering a function with arguments, I can't pass the letter to the function, and I can not make a custom method for every single button, because I would need to process first button presses and second button presses differently to make the connections. Is there a way to use a single function in the command but customize it to each button? Alternatively, if there isn't, are there any other solutions I can try? I am willing to try using other graphics packages as well.

Tkinter button issue

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()

How do I disable keyboard input into an Entry widget, disable resizing of tkinter window and hide the console window?

I am making a calculator using tkinter and I wish to do the following:
Disable keyboard input for the Entry widget so that the user can only input through the buttons.
Even after disabling keyboard input for the Entry widget, I wish to be able to change the background and foreground of the widget.
I wish to hide the console window because it is totally useless in the use of this calculator.
I don't want to let the user resize the root window. How do I disallow the resizing of the root window?
Here is my code so far...
from tkinter import *
root = Tk()
root.title("Calculator")
root.config(background="black")
operator = ""
textVar = StringVar()
def valInput(number):
global operator
operator+=str(number)
textVar.set(operator)
display = Entry(root, textvariable=textVar, font=("Arial", 14, "bold"), bg="lightblue", fg="black", justify="right")
display.grid(row=0, column=0, columnspan=4)
btn7 = Button(root, font=("Arial", 12, "bold"), bg="orange", fg="red", text="7", command= lambda : valInput(7))
btn7.grid(row=1, column=0)
"""
And more buttons...
"""
root.mainloop()
As you can see, I can input into the Entry widget using buttons but later on, after the calculator is complete, if the user inputs characters like abcd... it will cause problems and show errors. How do I disallow keyboard entry so that I can avoid these errors?
I want to make my calculator a bit colorful. I changed the color of the root window, the buttons and also the color of the Entry widget. Is there any way to change the color of the widget even after it is disabled?
I don't need the console window while using this calculator. How do I hide it?
If I resize the root window, the calculator becomes ugly, besides, resizing the window isn't necessary. So how do I prevent the user from resizing the window?
To be able to disable keyboard input in Entry(args)
Set the state to disabled:
display = Entry(root, state=DISABLED)
To be able to disable the feature of resizing the tkinter window (so that you can't drag and stretch it.
root.resizable(0,0)
To be able to make the command prompt window disappear. (I just want the tkinter window.
Rename the file with a .pyw extension (assuming you are using windows)
Don't use from tkinter import * it's really not recommended because it pollutes the main namespace with every public name in the module. At best this makes code less explicit, at worst, it can (and it will) cause name collisions.
Have the right reflexes, use import tkinter or import tkinter as tk instead
this should work, you have to use the disabledbackground option :
import tkinter as tk
root = tk.Tk()
display = tk.Entry(root,font=('Arial', 20, 'bold'), disabledbackground='lightblue', state='disabled')
display.pack()
root.resizable(0,0)
root.mainloop()

Simple bind function doesn't work in python

I'm currently creating an adventure game and I want to bind alt+a to my callback. It doesn't do what I want, so I have two questions:
Is it possible to bind a function to a Label, too?
Why does this (simplyfied) code doesn't work?
Here is the code:
import tkinter as tk
dw = tk.Tk()
dw.title('Hearts')
def play(event):
print('This is the test.')
areal = tk.Frame(master=dw, width=1200, height=600, bg='blue')
areal.pack_propagate(0)
areal.pack(fill=tk.BOTH, expand=bool(dw))
areal.bind("<Alt-A>", play)
dw.mainloop()
It doesn't give me an error, but it doesn't do anything when I click the Frame and afterwards press alt+a. What is wrong here?
EDIT:
import tkinter as tk
def go_fwd(event):
areal.focus_set()
print(event.x, event.y)
dw = tk.Tk()
dw.title('Adventure')
areal = tk.Frame(master=dw, width=20000, height=600, bg='blue')
areal.pack_propagate(0)
areal.pack(fill=tk.BOTH, expand=bool(dw)-100)
areal.focus_set()
dw.bind("<Alt-A>", go_fwd)
enter = tk.Frame(master=dw, width=20000, height=100, bg='cyan')
enter.pack(fill=tk.X)
enterentry = tk.Text(master=enter, width=100, height=4, bg='white')
enterentry.pack()
enterbutton = tk.Button(master=enter, text='Senden', bg='red')
enterbutton.pack()
dw.mainloop()
Here is the complete code.
Is it possible to bind a function to a Label, too?
You can bind to any widget you want. However, if you bind key events, the bindings will only work if the widget has focus. By default, most widgets other than Entry and Text don't get focus unless you explicitly set the focus to them.
Note: only one widget can have keyboard focus at a time.
You can also set a binding to the root window, which will cause it to fire no matter what widget has focus.
For a more thorough explanation of how key bindings are processed, see this answer: https://stackoverflow.com/a/11542200/7432
Why does this (simplyfied) code doesn't work?
It doesn't work the way you expect because the binding is on a Frame widget, but that widget doesn't have the keyboard focus. You could give it focus with something like this:
areal.focus_set()
Or, you could only give it focus after you click on the frame, by creating a binding on a mouse click:
areal.bind("<1>", lambda event: areal.focus_set())
Note: you are binding to a capital "A", so make sure when you test that you're pressing control-alt-a
You need to bind to dw instead of your frame.
So, you can do dw.bind("<Alt-A>", play).
A minor note, Alt-A will bind to the uppercase A as expected, so you'd have to click Alt+Shift+A on your keyboard. Doing Alt+A on your keyboard won't work, you'd have to bind to Alt-a for this to work.
The main window has keyboard focus. Or, alternatively you can leave the bind on the frame and just do areal.focus_set() to set the focus to the frame.

tkinter.Button retains depressed appearance after exiting event handling

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

Categories