Tkinter: How to disable a button after it is pressed? - python

So I am trying to make hangman in python using Tkinter and I have mostly finished the game but, I want to add a feature that disables the button once it is pressed so that the user doesn't guess the same letter twice. Any idea how I can implement it.
the code:https://drive.google.com/file/d/1v0tjlSZC_xHCQ0WopNPRJC1pLNaQ4wRR/view?usp=sharing

Not only do you overwrite the button object when you create all the buttons, the button object will also be None because place() doesn't return anything.
Split that line in two:
button = Button(...) and
button.place(...)
But even then the button object is overwritten for each new button, so the button object takes the value of the last button 'z'.
To disable individual buttons, you need to be able to access each of them, e.g. by putting all objects in a list: button[n] = Button(...) Then in the guess() function you must disable exactly that button that was pressed.

Related

Is it possible to destroy a button or checkbox when it is clicked?

I am currently using tkinter to build a GUI and one of the functionalities I was hoping to achieve with the buttons was if it can be destroyed when it is clicked. I tried something along the lines of:
button = Button(window, text="hello", command=button.destroy()
This doesn't work as I'm getting the error:
UnboundLocalError: local variable 'button' referenced before assignment.
Are there are workarounds to accomplish a task like this?
You need to save a reference to the button, and then call the destroy method on the button. Here's one way:
button = Button(window, text="hello")
button.configure(command=button.destroy)
The problem in your code is that you're trying to use button before the button has been created. Creating the button and then configuring the button in a separate step works around that problem since button exists by the time the second line is called.
Also notice that command is set to button.destroy, not button.destroy(). With the parenthesis, you're going to immediately call the method instead of assigning the command to the button.
That depends.. there is the .grid_forget method (or .pack_forget using pack instead of grid) if you don't want to delete permanently the widget (you can re-add it with .grid), and also the .grid_remove or pack.remove, but otherwise I suggest you to use .destroy().

receiving selected choice on Tkinter listbox

I'm trying to build a listbox using Tkinter and receive the selected option by clicking it.
import Tkinter as tk
from Tkinter import *
root = tk.Tk()
lst=Listbox(root, height=30, width=50)
lst.insert(1, "hy")
lst.insert(2, "hello")
lst.insert(3, "hey")
lst.pack()
sel = lst.curselection()
print sel
root.mainloop()
However, when I run the code it prints me an empty tuple before I pressed any choise.
Does someone know how to get the selected choise after I press one and not right after I run it?
Thanks a lot :)
You are getting the selection about a millisecond after creating the widget, well before the user has a chance to see the UI much less interact with it.
GUI programs are event based, meaning that things happen in response to events. Events are things like clicking buttons, inserting data into input widgets, and selecting items from listboxes.
You need to do one of two things: create a button or other widget which will get the selected item, or configure it so that a function is called whenever an item is selected.
No matter which solution you use, you will need a function that ultimately calls the curselection method of the listbox to get a list of indices. You can then call the get method to get the selected item or items.
Here's a function definition that will print the selected item, or print "no selection" if nothing is selected. So that it can be resused without modification. we'll define it to take the listbox as an argument.
Note: this example assumes the widget only supports a single select, to keep it simple:
def print_selection(listbox):
selection = listbox.curselection()
if selection:
print(f"selected item: {listbox.get(selection[0])}")
else:
print("nothing is selected")
Using a button
To call this from a button is straight-forward. We just create a button after we create the listbox, and use the command attribute to call the function. Since the function we wrote earlier needs a parameter, we'll use lambda to create a temporary function for the button.
button = tk.Button(root, text="Print Selected Item", command=lambda: print_selection(lst))
button.pack()
Calling the function when the selection is made
To call the function whenever the user changes the selection, we can bind a function to the <<ListboxSelect>> event. We'll create a separate function for this, and then pull the widget from the event object that is automatically passed to the function.
def print_callback(event):
print_selection(event.widget)
lst.bind("<<ListboxSelect>>", print_callback)
First of all, the reason you are getting an empty tuple is because you have executed the statements:
sel = lst.curselection()
print(sel)
before you have executed the root.mainloop()
Secondly, your setup for listbox fails to include a StringVar variable to hold your list.
Once the variable has been defined, you should be able to use the .insert statements to add your list items one at a time, or you can initialize the StringVar variable using a .set('hy', 'hello', 'hey') command.
To provide a return of a selected variable, you must incorporate an event handler to determine the list position selected onclick or some other triggering method.
For a pretty clear explanation of these characteristics check here

Issue with selectively undoing functions in Python

I have created a program in the turtle canvas in which the user can press any letter key on the keyboard and the turtle draws the corresponding letter in the canvas. I have also implemented an undo function that undoes the last function called by clearing the canvas then redrawing everything up until the point before the undone action. This undo function works by the user pressing a tkinter button at the bottom of the canvas labeled "Undo" or pressing the "left" key on the keyboard.
However, I have also decided to create a dynamic (a.k.a. selective, nonlinear, etc.) undo method in which there is a tkinter drop down menu and each drawing function, as it is called, is written to that menu. Then, from the menu, the user can select the function she previously called that he/she wants to undo and the program will undo that specific function instance. The whole process is described below:
The tkinter drop down menu is created through the following code block:
# global variables because next code block located ABOVE this one
global fav
fav = Menubutton(text = "Selective redo", state = DISABLED)
fav.pack(side = "left")
fav.menu = Menu(fav, tearoff = 0)
fav["menu"] = fav.menu
global redo1
redo1 = fav.menu
fav.pack()
When letter drawn, that letter's point object written to menu using code block below:
po = Point(v,y,c,w,isdown(),x,ph,pw)
undo1.add_command(label = Point.__str__(po), command = lambda: selectundo(undo1.index(po)))
# 'po' is a Point object for each function, and the string of that is the label of the menu item
Point.__str__() is this Point object method:
def __str__(self):
return "({})".format(self.function)
Finally, user supposed to be able to select –from menu– which instance of letter to undo, and program undoes that letter instance using the user-defined function below:
def selectundo(x):
for ty in range(x, len(function)):
undoHandler()
update()
listen()
The issue here is that when the user chooses to draw two or more of the same letters, they both get written to the menu with the same exact index values and thus, if the user wants to undo say, 1 of those, it will instead undo ALL instances of the letter from the canvas, not just the one selected by the user! However, I want the program to ONLY undo the instance of the letter that the user selects from the menu. Any ideas on how I would fix this issue? Any help is much appreciated! :)

Is there a way to press a button without touching it on tkinter / python?

Hi I need to do this because, I am making a matching / memmory game, and there has to be a button (Totally separated from the ones on the current game) that when I press it, it has to show the matching cards automatically without having to touch the buttons with the mouse.
Is there a "press" function or something like that for pressing the button?
Thanks! :)
As Joel Cornett suggests in a comment, it might make more sense to simply call the callback that you passed to the button. However, as described in the docs, the Button.invoke() method will have the same effect as pressing the button (and will return the result of the callback), with the slight advantage that it will have no effect if the button is currently disabled or has no callback.
If you also want visual feedback for the button you can do something like this:
from time import sleep
# somewhere the button is defined to do something when clicked
self.button_save = tk.Button(text="Save", command = self.doSomething)
# somewhere else
self.button_save.bind("<Return>", self.invoke_button)
def invoke_button(self, event):
event.widget.config(relief = "sunken")
self.root.update_idletasks()
event.widget.invoke()
sleep(0.1)
event.widget.config(relief = "raised")
In this example when the button has focus and Enter/Return is pressed on the keyboard, the button appears to be pressed, does the same thing as when clicked (mouse/touch) and then appears unpressed again.

Function when entered in text box?

I'm using Tkinter for a small Python application. It has a set of ratio buttons, a text box, and a button. Is there a way to make it so the user can simply press Enter/Return on a keyboard and run the same function the button runs? The text box stays selected even when a radio is changed, so that won't cause any problems.
You should be able to bind an event handler to either the text box widget or the whole application that will be called when the event happens. Assuming you have a function to handle the event, something along the lines of:
widget.bind('<Return>', event_handler)
You can also bind a handler function at the application level by calling the bind_all() method of any widget, e.g.:
self.bind_all('<Return>', self.event_handler)
Note the key name is Return not Enter. See Key Names for a list of them all. You can also prefix the key name with a modifier like Shift- and Control- if desired.
There's a decent online reference for tkinter 8.4 here.

Categories