Python tkinter button event - python

import tkinter
import random
# GUI
window = tkinter.Tk()
window.title("Hangman")
window.geometry("640x400+100+100")
frame = tkinter.Frame(window)
frame.pack()
button_animal = tkinter.Button(frame)
button_animal['text'] = "Animal"
button_animal['background'] = 'yellow'
button_animal.pack()
button_capital = tkinter.Button(frame)
button_capital['text'] = "Capital"
button_capital['background'] = 'blue'
button_capital.pack()
This is what I wrote so far, what am I supposed to do to make the button to make event happens?
if I click animal, it should play a game, but I am not sure how to do at first.

You should add command parameter to your button initialization. You can find more information in here tkinter button widget
Basically, you can bind your button to function in two ways:
button_animal = tkinter.Button(frame, command=your_function)
or if your function requires arguments, you can use lambda, like:
button_animal = tkinter.Button(frame, command=lambda : your_function(arg))
If you want to bind multiple function, you can do following:
button_animal = tkinter.Button(frame, command=lambda :[funct1(arg),funct2(arg)])

Related

how to change the color of PanedWindow upon hovering over it, for multiple PanedWindow in tkinter?

I am trying to make PanedWindow change color when I hover mouse over it in tkinter.
now this works for a single iteration.
but when i try to do it for multiple panedwindows it only changes color of the last window.
import tkinter as tk
root = tk.Tk()
for i in range(10):
m1 = tk.PanedWindow(root, bd=4, relief="flat", bg="blue")
m1.pack()
def on_enter(e):
m1.config(background='OrangeRed3', relief="flat")
def on_leave(e):
m1.config(background='SystemButtonFace', relief="flat")
# Create a Button
button1 = tk.Button(m1, text=f"{i}")
button1.pack(pady=20)
# Bind the Enter and Leave Events to the Button
m1.bind('<Enter>', on_enter)
m1.bind('<Leave>', on_leave)
m1.add(button1)
tk.mainloop()
Since at each iteration of the loop all variables are overwritten, the functions are bound to the last created element. It is necessary to pass the desired element to the function. It is even better to collect everything created in dictionaries, so that in the future you can easily change them.
import tkinter as tk
from functools import partial
ms = {}
btns = {}
root = tk.Tk()
def on_enter(m, e):
m.config(background='OrangeRed3', relief="flat")
def on_leave(m, e):
m.config(background='SystemButtonFace', relief="flat")
for i in range(10):
ms[i] = tk.PanedWindow(root, bd=4, relief="flat", bg="blue")
ms[i].pack()
# Create a Button
btns[i] = tk.Button(ms[i], text=f"{i}")
btns[i].pack(pady=20)
# Bind the Enter and Leave Events to the Button
ms[i].bind('<Enter>', partial(on_enter, ms[i]))
ms[i].bind('<Leave>', partial(on_leave, ms[i]))
ms[i].add(btns[i])
tk.mainloop()

How to identify labels on a Python Tkinter Tab in the event handler

I am placing labels on a Tab in Tkinter with a for loop. How can I identify in the event handler which label was clicked (or its loop index)? I guess it is functionally similar to a ListBox but without the formatting restrictions. I might want to put the labels in a circle or place them diagonally. I tried finding the coordinates of the label but these are available only if the tab is the first one visible or the tab is redrawn when made active. Also the x, y passed in the event handler is the x, y within the label which does not help to identify the label.
I could copy the label code about 10 times and and have about 10 event handlers. This would work but this is no longer 1970!
Perhaps I could bind a handler to the tab canvas and identify the label from its coordinates. The label would need to be on the first tab or the tab drawn when active.
Perhaps I could create a different event handler for each label by holding the event handlers in an array. I would need an event handler for each label. The code would need to change if the number of labels changed.
I am currently trying a label with ''. Would using buttons with command be easier?
What simple part of Python am I missing? I cannot be the first person to need this! Any help or advice would be appreciated.
You can save a reference to the label text for each label widget in a dict.
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
root.geometry('+800+50')
notebook = ttk.Notebook(root, width=300, height=200, padding=[10,10,10,10])
notebook.pack()
tab_one = tk.Frame(notebook, bg='wheat')
notebook.add(tab_one, text='Cheese', padding=[10,10,10,10])
tab_two = tk.Frame(notebook, bg='mint cream')
notebook.add(tab_two, text='Misc.', padding=[10,10,10,10])
def clicked(event):
print('Clicked:', name_dict[event.widget])
# Populating Cheese tab with clickable Labels
name_list = ['Cheddar', 'Ilchester', 'Limburger']
name_dict = {}
for index, name in enumerate(name_list):
a = tk.Label(tab_one, width=10, text=name, bg='peru')
a.grid(row=index, column=0, padx=5, pady=5)
name_dict[a] = name # Save references in a dict
a.bind('<Button-1>', clicked)
tk.Label(tab_two, text='Just some text...', bg='powder blue').pack(anchor='nw')
root.mainloop()
Is this what you had in mind?
When you bind events, the function receives an object that includes a reference to the widget that received the event. In the following example, notice how it uses event.widget to refer to the widget that was clicked on.
import tkinter as tk
def update_label(event):
event.widget.configure(text="You clicked me")
root = tk.Tk()
for i in range(10):
l = tk.Label(root, text="Click me", width=20)
l.pack()
l.bind("<1>", update_label)
root.mainloop()

Tkinter Option Menu Variable.get() method not returning selected element

I have some problems with Tkinter, I want so retrieve the selected item of an option menu when pressing a button, but when testing the funcionality with a small testfuntion 'getdataset', I only get the predefined datavariable ('Birthdata') as an output so it seems that the datavariable.get() method is not returning the selected option in the optionmenu. I have looked everywhere but cannot seem to find the answer. Any help is appreciated.
Code with option menu
root = Tk()
root.configure(background='white')
def getdataset():
print(datavariable.get())
datavariable = StringVar(root)
datavariable.set('Birthdata')
PickData = OptionMenu(root, datavariable,'Birthdata', 'Marriagedata', 'Deathdata',command=getdataset)
PickData.grid(column=1,columnspan=2,row=1)
Code to initialize test function with button click:
wordcloud = Button(root, text = 'Word Cloud', bg='white', width=20, height = 5, command=getdataset)
Output after multiple button clicks:
Birthdata
Birthdata
Birthdata
I w
You should be getting an error when selecting an option from your OptionMenu.
Specifically:
TypeError: getdataset() takes 0 positional arguments but 1 was given
To fix that we can add a argument that will handle this. Update your function to look like this:
def getdataset(_=None):
The reason for this is due to how your 2 different commands are interacting with this function. The OptionMenu command is sending an argument to the function when called where the Button command is not. This is not obvious at first but if you write something like:
def getdataset(arg=None):
print(arg)
You will see that when you press the Button it will print None and when you select something from the menu it will print the value of the selection.
You code should look something like this:
import tkinter as tk
def getdataset(_=None):
print(datavariable.get())
root = tk.Tk()
root.configure(background='white')
datavariable = tk.StringVar(root)
datavariable.set('Birthdata')
tk.OptionMenu(root, datavariable, 'Birthdata', 'Marriagedata', 'Deathdata', command=getdataset).grid()
tk.Button(root, text='Word Cloud', bg='white', width=20, height=5, command=getdataset).grid()
root.mainloop()

Change the colour of a tkinter button generated in a loop

In python tkinter, I've got a program that generates multiple buttons with a default fg of red
from tkinter import *
root = Tk()
def dothis(i):
print(i)
button.config(fg='green')
for i in range(5):
button = Button(root, width=30, text="button{}".format(i), command=lambda i=i: dothis(i))
button.config(fg='red')
button.pack()
This creates this window:
In this program, I have attempted to make it so that once the button is pressed, the colour of the text (fg) turns green. Instead, when dothis(i) is called, it changes the colour of the last button generated to green. This is not what I want.
To summarise, when I click button3, I want to see this:
But instead, I see this (the last generated button is modified, not the one I want):
How can I work around this, while still keeping the buttons generated in a loop?
Note: The buttons must also be modifiable after changing the colour e.g. Once changed to green, it can be turned back to red.
You got the correct lambda expression, but the parameter you passed isn't related to the buttons you created. You should pass the Button widget as a parameter instead:
from tkinter import *
root = Tk()
def dothis(button):
button.config(fg='green')
for i in range(5):
button = Button(root, width=30, text="button{}".format(i))
button.config(fg='red', command=lambda i=button: dothis(i))
button.pack()
root.mainloop()
To achieve toggling between red and green, you can use ternary operator:
def dothis(button):
button.config(fg='green' if button["foreground"]=="red" else "red")
If you insist on all buttons except the last one being anonymous, and using command instead of binding events, you can use partials:
from tkinter import *
from functools import partial
root = Tk()
button_callbacks = {}
def on_click(button):
button.config(fg="green")
for i in range(5):
button = Button(root, width=30, text=f"button{i}", fg="red")
callback_name = f"on_click_{i}"
button_callbacks.update({callback_name: partial(on_click, button=button)})
button.config(command=button_callbacks[callback_name])
button.pack()
Using event binding would be a bit more straight-forward, but the behavior is not exactly the same as triggering a callback using command. Here's what that might look like:
from tkinter import *
root = Tk()
def on_click(event):
button = event.widget
button.config(fg="green")
for i in range(5):
button = Button(root, width=30, text=f"button{i}", fg="red")
button.bind("<Button-1>", on_click)
button.pack()

Create functions to pack one widget and remove all others when different keys are pressed

I am making a controller for a car with Python and I was going to have 3 separate images to represent whether the wheels are turning left, right, or neutral. I need only one of these images to be shown at a time.
So far I have used bind to trigger functions because I haven't seen any other way to do so. I have looked into pack and pack_forget but I don't know how I could trigger them to be activated by other widgets (since I am using bind).
import tkinter as tk
win = tk.Tk()
def forwards(event):
print("going forwards...")
def left(event):
print("turning left...")
def right(event):
print("turning right...")
def backwards(event):
print("going backwards...")
neutralImage = tk.PhotoImage(file="neutral.gif")
leftImage = tk.PhotoImage(file="turnedLeft.gif")
rightImage = tk.PhotoImage(file="turnedRight.gif")
neutralPosition = tk.Label(win, image=neutralImage)
leftPosition = tk.Label(win, image=leftImage)
rightPosition = tk.Label(win, image=rightImage)
win.bind("w", forwards)
win.bind("a", left)
win.bind("d", right)
win.bind("s", backwards)
I have identified the problem as the following: I can't hide or show the widgets unless it is them that I press the button over.
Instead of having three widgets what you can do is replace the image of the same widget when you need it.
import tkinter as tk
def changeImage(imageLabelWidget, newImage):
imageLabelWidget.configure(image=newImage)
imageLabelWidget.image = newImage
win = tk.Tk()
neutralImage = tk.PhotoImage(file="neutral.gif")
leftImage = tk.PhotoImage(file="turnedLeft.gif")
rightImage = tk.PhotoImage(file="turnedRight.gif")
neutralPosition = tk.Label(win, image=neutralImage)
neutralPosition.image = neutralImage
neutralPosition.pack()
win.bind("w", lambda event, imageLabelWidget=neutralPosition, newImage=neutralImage:
changeImage(imageLabelWidget, newImage))
win.bind("a", lambda event, imageLabelWidget=neutralPosition, newImage=leftImage:
changeImage(imageLabelWidget, newImage))
win.bind("d", lambda event, imageLabelWidget=neutralPosition, newImage=rightImage:
changeImage(imageLabelWidget, newImage))
win.mainloop()

Categories