How to update the gui while the program is running in tkinter? - python

Essentially I just want to update the grid on the GUI while the Tkinter program is running, the changes I want to make while the program is running are under the return true part in the function but I can't find how to do this.
import random
from tkinter import *
def input_validation(coordinates, user_input):
if coordinates [0] <0 or coordinates [1] <0 or coordinates [0] >2 or coordinates [1] >2:
pass
elif (int(user_input) == int(frame.grid_slaves(coordinates[0], coordinates[1])[0]['text'])):
return True
Label (frame, text = frame.grid_slaves(coordinates[0], coordinates[1])[0]['text']
).grid(row= previous_coordinates[0], column= previous_coordinates[1])
Label (frame, text = "").grid(row= coordinates[0], column= coordinates[1])
def button_click():
if (input_validation(coordinates_up, number_input.get()) == True):
pass
elif(input_validation(coordinates_left, number_input.get()) == True):
pass
elif(input_validation(coordinates_right, number_input.get()) == True):
pass
elif(input_validation(coordinates_down, number_input.get()) == True):
pass
else:
print("hello")
text_display.configure(text="Please input a number that is surrounding the empty space")
puzzle = Tk()
puzzle.title("Eight Puzzle")
frame = Frame(puzzle)
space_coordinates = [2,2]
frame.pack()
number_input= Entry(frame, width= 20)
number_input.grid(row = 5, column =7)
button = Button(frame, text="Enter", command = button_click)
button.grid(row = 6, column = 7)
number = 8
text_display = Label(frame, text="Input the number you want to move into the empty space \n *make sure the number is next to the empty space*", fg="red")
text_display.grid(row = 3, column = 7)
for i in range (0,3):
for a in range (0,3):
if number == 0:
Label (frame, text = " ").grid(row=i, column=a)
else:
Label (frame, text = number).grid(row=i, column=a)
number= number -1
previous_coordinates = []
previous_coordinates.append(space_coordinates[0])
previous_coordinates.append(space_coordinates[1])
coordinates_up = [previous_coordinates[0], previous_coordinates[1]-1]
coordinates_left = [previous_coordinates[0]-1, previous_coordinates[1]]
coordinates_right = [previous_coordinates[0]+1, previous_coordinates[1]]
coordinates_down = [previous_coordinates[0],previous_coordinates[1]+1]

You will need puzzle.mainloop() at the end of your tkinter program. Also, if you want to update the window mid-function, you can use: window.update()

Related

How to fix positioning of labels in tkinter?

So I am trying to have a GUI for my Converter (it is intentionally meant to go up to 255 aka 8bits)
And I have got it to work on the button layout as planned. But to make it more user-friendly I wanted to put a 'Convert From' label/text above the buttons. However, as soon as I shifted the buttons down a row it didn't go as planned and after careful examination of it. I have got nowhere.
image reference of how it looks without label above the buttons (row = 0)
image reference of how it looks with the label above the buttons
as you can see it takes it off screen and out of uniform (row = 1)
#Converter GUI
#importing necessary libaries
import tkinter
import tkinter.ttk
import tkinter.messagebox
import sys
#defining the types of conversion
def bin2den(value):
if len(value) != 8: #checks that it has a length of 8 binary digits
return "None"
return int(value, 2)
def bin2hex(value):
if len(value) != 8: #checks that it has a length of 8 binary digits
return "None"
Ox = hex(int(value, 2))[2:].upper()
Ox = Ox.zfill(2)
return Ox
def bin2bin(value):
if len(value) != 8: #checks that it has a length of 8 binary digits
print("Invalid input, 8 bits required!")
return "None"
return value.zfill(8)
def den2bin(value):
if int(value) > 255 or int(value) < 0:
return "None"
Ob = bin(int(value))[2:]#removing the ob prefix
filled = Ob.zfill(8) #filling it to become an 8 bit binary if worth less than 8 bits
return filled
def den2hex(value):
if int(value) > 255 or int(value) < 0:
return "None"
Ox = hex(int(value))[2:].upper() #removing the ox prefix and capitalising the hex output
Ox = Ox.zfill(2)#filling the output if not double digits
return Ox
def den2den(value):
if int(value) > 255 or int(value) < 0:
print("Out Of Range")
return "None"
return value
def hex2bin(value):
while len(value) != 2 and len(value) > 2: #checking if hex value outside of ff
return "None"
Ob = bin(int(value, 16))[2:] #removing the ob prefix
Ob = Ob.zfill(8)#filling binary to be 8bits if value is below 8 bits
return Ob
def hex2den(value):
while len(value) != 2 and len(value) > 2: #checking if hex value outside of ff
return "None"
return int(value, 16)
def hex2hex(value):
while len(value) != 2 and len(value) > 2: #checking if hex value outside of ff
print("Invalid input, try again")
return "None"
value = value.upper() #capitaliseing for formality
return value
def readFile(fileName):
fileObj = open(fileName, "r")#opens file in read only mode
HexArray = fileObj.read().splitlines()#puts file into an array
fileObj.close()
return HexArray
#setting main window class
#and defining main attributes of the window
class MainWindow():
FONT = ("Consolas", 16)
TxtMaxLen = 32
def __init__(self):
self._window = tkinter.Tk()
self._window.title("Converter")
#self._window.geometry("800x240") redundant as set below due to positioning
self._window["bg"] = "#20A3FF" #background colour
self._window.resizable(False, False) #stops the window being resized
windowWidth = self._window.winfo_reqwidth()
windowHeight = self._window.winfo_reqheight()
# Gets both half the screen width/height and window width/height
positionRight = int(self._window.winfo_screenwidth()/2 - windowWidth/2)-330
positionDown = int(self._window.winfo_screenheight()/2 - windowHeight/2)-200
self._window.geometry(f"{windowWidth}x{windowHeight}+{positionRight}+{positionDown}")
self._window.geometry("800x240")#setting window size
label = tkinter.Label(self._window, text="Number: ", font=MainWindow.FONT)#label defined for number input box
label.grid(row=0, column=0, padx=10, pady=5)#positioning / dimensions of input box
self._txt_input = tkinter.Entry(self._window, width=MainWindow.TxtMaxLen, font=MainWindow.FONT) #defined input box
self._txt_input.grid(row=0, column=1, pady=5)#postioning / dimensions of input box
self._txt_input.focus()
#this is the label that is just runining it
#label = tkinter.Label(self._window, text="Convert From ", font=MainWindow.FONT)
#label.grid(row=0, column=2, padx=5, pady=5)#positioning / dimensions of input box
#following 6 bits of code when row = 0 it works fine but when shifted down a row it goes wrong
self._bt_bin = tkinter.Button(self._window, text="Bin", font=MainWindow.FONT, command=self.BinSelection)#button defined for bin
self._bt_bin.grid(row=0, column=2, padx=5, pady=5)#postioning / dimensions of button box
self._bt_den = tkinter.Button(self._window, text="Den", font=MainWindow.FONT, command=self.DenSelection)#button defined for den
self._bt_den.grid(row=0, column=4, padx=5, pady=5)#postioning / dimensions of button box
self._bt_hex = tkinter.Button(self._window, text="Hex", font=MainWindow.FONT, command=self.HexSelection)#button defined for bin
self._bt_hex.grid(row=0, column=6, padx=5, pady=5)#postioning / dimensions of button box
separator = tkinter.ttk.Separator(self._window,orient=tkinter.HORIZONTAL)
separator.grid(row=1, column=1, pady=4)
#setting the Output boxes and the labels accordingly
#binary output box and label
label = tkinter.Label(self._window, text="Binary:", font=MainWindow.FONT)#label defined for number box
label.grid(row=2, column=0, padx=10, pady=5)
self._stringvar_bin = tkinter.StringVar()
txt_output = tkinter.Entry(self._window, textvariable=self._stringvar_bin, width=MainWindow.TxtMaxLen, state="readonly", font=MainWindow.FONT)#entry box set to readonly to act as a display box
txt_output.grid(row=2, column=1, pady=5)
#denary output box and label
label = tkinter.Label(self._window, text="Denary:", font=MainWindow.FONT)#label defined for number box
label.grid(row=3, column=0, padx=5, pady=5)
self._stringvar_den = tkinter.StringVar()
txt_output = tkinter.Entry(self._window, textvariable=self._stringvar_den, width=MainWindow.TxtMaxLen, state="readonly", font=MainWindow.FONT)
txt_output.grid(row=3, column=1, pady=5)
#hexadecimal output box and label
label = tkinter.Label(self._window, text="Hexadecimal:", font=MainWindow.FONT)#label defined for number box
label.grid(row=4, column=0, padx=5, pady=5)
self._stringvar_hex = tkinter.StringVar()
txt_output = tkinter.Entry(self._window, textvariable=self._stringvar_hex, width=MainWindow.TxtMaxLen, state="readonly", font=MainWindow.FONT)
txt_output.grid(row=4, column=1, pady=5)
def BinSelection(self):
try:
Bin = self._txt_input.get().strip().replace(" ", "")
BinValue = bin2bin(Bin)
DenValue = bin2den(Bin)
HexValue = bin2hex(Bin)
self._set_values(BinValue, DenValue, HexValue)
except Exception as ex:
tkinter.messagebox.showerror("Error", "Invalid conversion")
tkinter.messagebox.showinfo("Error", "Enter a valid 8 bit binary number or a integer / hexadecimal value")
print(ex, file=sys.stderr)
def DenSelection(self):
try:
Den = self._txt_input.get().strip().replace(" ", "")
DenValue = den2den(Den)
BinValue = den2bin(Den)
HexValue = den2hex(Den)
self._set_values(BinValue, DenValue, HexValue)
except Exception as ex:
tkinter.messagebox.showerror("Error", "Invalid conversion")
print(ex, file=sys.stderr)
def HexSelection(self):
try:
Hex = self._txt_input.get().strip().replace(" ", "")
HexValue = hex2hex(Hex)
BinValue = hex2bin(Hex)
DenValue = hex2den(Hex)
self._set_values(BinValue, DenValue, HexValue)
except Exception as ex:
tkinter.messagebox.showerror("Error", "Invalid conversion")
print(ex, file=sys.stderr)
def _set_values(self, BinValue, DenValue, HexValue):
if not BinValue.startswith(""):
BinValue = "" + BinValue
if not HexValue.startswith(""):
HexValue = "" + HexValue
self._stringvar_bin.set(BinValue)
self._stringvar_den.set(DenValue)
self._stringvar_hex.set(HexValue)
def mainloop(self):
self._window.mainloop()
if __name__ == "__main__":
win = MainWindow()
win.mainloop()
any insight on how to fix this would be great thanks. and sorry if this whole question was just a silly one.
Answer to my question as the Issue is now resolved. Fixed it using columnspan which allows you to set a box across several columns.
label = tkinter.Label(self._window, text="Convert From ", font=MainWindow.FONT)
label.grid(row=0, column=2,columnspan=3, padx=5, pady=5)
Here is what it looks like with the resolved problem

Beginners Problem! NameError on python tkinter

New to python so this is probably a dumb question...
I want to say that if text1.get() == "1" then x = 0 and if text2.get() = "2" then y = 8. Then I want a button that adds x and y.
from tkinter import *
new = Tk()
new.title("Lockdown?")
new.geometry("700x400")
text3=Entry(new, width = "60")
text3.place(rely=0.15)
if text3.get() == "":
label9 = Label(new, text="required field", fg="red")
label9.place(rely=0.17)
elif text3.get() == "1":
x = "0"
elif text3.get() == "2":
x = "1"
elif text3.get() == "10":
x = "9"
else:
label10 = Label(new, text = "Please use a number ranging from 1-10")
text4=Entry(new, width = "60")
text4.place(rely=0.26)
if text4.get() == "":
label11 = Label(new, text="Required Field", fg = "red")
label11.place(rely=0.19)
elif text4.get() == "7":
y = "3"
elif text4.get() == "8":
y = "2"
elif text4.get() == "9":
y = "1"
elif text4.get() == "10":
y = "0"
else:
label11 = Label(new, text = "Please use a number ranging from 1-10")
def cmd3():
label15 = Label(new, text = x + y)
label15.place(rely=0.8)
btn3 = Button(new, text = "Submit Answers", command = cmd3, font=("Arial Bold", 25), bg = "white", fg = "black")
btn3.place(rely=0.71)
new.mainloop()
GUI frameworks (tkinter, PyQt, wxPython, etc., and in other languages) don't work like function input(). Entry doesn't wait for your data - it only inform mainloop what it has to display in window. And mainloop starts all - it displays window, etc. If you want to get value from Entry then you should do it in function assigned to Button.
My version with other changes
I use pack() instead of place() because it is simpler to organize widgets.
I create all labels at start (with empty text) and later I replace text in labels.
import tkinter as tk # PEP8: `import *` is not preferred
# --- functions ---
def cmd3():
# default value at start
x = None
y = None
# remove previous text
label1['text'] = ""
label2['text'] = ""
text = text1.get()
if text == "":
label1['text'] = "required field"
elif text == "1":
x = 0
elif text == "2":
x = 1
elif text == "10":
x = 9
else:
label1['text'] = "Please use a number ranging from 1-10"
text = text2.get()
if text == "":
label2['text'] = "required field"
elif text == "7":
y = 3
elif text == "8":
y = 2
elif text == "9":
y = 1
elif text == "10":
y = 0
else:
label2['text'] = "Please use a number ranging from 1-10"
print(x, y)
# it can't be `if x and y:` because `0` will gives `False`
if (x is not None) and (y is not None):
label_result['text'] = str(x + y)
# --- main ---
new = tk.Tk()
# empty at start
label1 = tk.Label(new, text="", fg="red")
label1.pack()
text1 = tk.Entry(new, width="60") # PEP8: arguments without spaces around `=`
text1.pack()
# empty at start
label2 = tk.Label(new, text="", fg="red")
label2.pack()
text2 = tk.Entry(new, width="60")
text2.pack()
btn3 = tk.Button(new, text="Submit Answers", command=cmd3)
btn3.pack()
# empty at start
label_result = tk.Label(new, text="", fg="green")
label_result.pack()
new.mainloop()

'int' object is not subscriptable when calling a function from a tkinter button

The error occurs once the button has been clicked and the function button_click() and input_validation() is run. I have tried putting int around the coordinates variables using brackets, in case they were not being treated as integers but that didn't work.
import random
from tkinter import *
def button_click():
def input_validation(coordinates, user_input):
if coordinates [0] <0 or coordinates [1] <0 and coordinates [0] >2 or coordinates [1] >2:
pass
elif (user_input == frame.grid_slaves(coordinates[0], coordinates[1])[0]['text']):
return True
Label (frame, text = frame.grid_slaves(coordinates[0], coordinates[1])[0]['text']
).grid(row= previous_coordinates[0], column= previous_coordinates[1])
Label (frame, text = "").grid(row= coordinates[0], column= coordinates[1])
if (input_validation(coordinates_up, number_input) == True):
pass
elif(input_validation(coordinates_left, number_input) == True):
pass
elif(input_validation(coordinates_right, number_input) == True):
pass
elif(input_validation(coordinates_down, number_input) == True):
text_display.configure(text="Please input a number that is surrounding the empty space")
puzzle = Tk()
puzzle.title("Eight Puzzle")
frame = Frame(puzzle)
space_coordinates = [2,2]
frame.pack()
number_input= Entry(frame, width= 20)
number_input.grid(row = 5, column =7)
button = Button(frame, text="Enter", command = button_click)
button.grid(row = 6, column = 7)
number = 8
text_display = Label(frame, text="Input the number you want to move into the empty space \n *make sure the number is next to the empty space*", fg="red")
text_display.grid(row = 3, column = 7)
for i in range (0,3):
for a in range (0,3):
if number == 0:
Label (frame, text = " ").grid(row=i, column=a)
else:
Label (frame, text = number).grid(row=i, column=a)
number= number -1
previous_coordinates = []
previous_coordinates.append(space_coordinates[0])
previous_coordinates.append(space_coordinates[1])
coordinates_up = previous_coordinates[1]-1
coordinates_left = previous_coordinates[0]-1
coordinates_right = previous_coordinates[0]+1
coordinates_down = previous_coordinates[1]+1
You are passing a single coordinate as the first argument to input_validation but you are trying to treat it like a list inside the function.
The solution is either to pass a list of coordinates into input_validation, or rewrite the function so that it properly treats coordinates as an int.

How to reference an attribute of a tkinter button which is inside of a list? (Python)

I'm making a calculator program and am creating the buttons to go in the calculator's interface. I create all buttons using one for loop initially, and then want to reconfigure certain buttons to change their colour and command. Is there any way to reference an attribute such as the "text" attribute of a button inside of a list? For example, if button index has a text attribute of "+" or "-" or "/" or "*" reconfigure colour to red.
The current code I have to do this is very inefficient as it requires each button to be referenced by index individually. If possible I would like to reference by an attribute and not just by index number. Algorithm in question can be found below.
#Method that creates calculator buttons, appends them to a list and packs them into the grid.
def create_number_buttons(self):
button_characters = "789*456/123-0.=+"
i = 0
self.button_list = []
for row_counter in range(2,6):
for column_counter in range(4):
self.button_list.append(Button(root, bg="#11708e", fg="white", activebackground="#11708e", pady=25, padx=35, text=button_characters[i], font=("Helvetica", 16, 'bold')))
self.button_list[i].grid(row=row_counter, column=column_counter, sticky="NSEW")
self.button_list[i].configure(command = lambda c=button_characters[i]: self.num_press(c))
i += 1
#Reconfigures the specific buttons.
self.button_list[3].configure(bg="#d14302", activebackground="#d14302")
self.button_list[7].configure(bg="#d14302", activebackground="#d14302")
self.button_list[11].configure(bg="#d14302", activebackground="#d14302")
self.button_list[13].configure(bg="#d14302", activebackground="#d14302")
self.button_list[14].configure(command=lambda: self.calculate_answer(), bg="#d14302", activebackground="#d14302")
self.button_list[15].configure(bg="#d14302", activebackground="#d14302")
Thanks for your help!
Full code below :)
from tkinter import *
from tkinter import messagebox
#Import so that I'm able to use regex to remove leading zeroes from the equation string when performing the calcultion.
import re
#Class to support logic of calculator, used for functionality and math operations.
class CalculatorFunctions:
def __init__(self, root):
self.root = root
self.answer = 0
#Command called if a number button is pressed.
def num_press(self, num):
new_input = num
self.text_box.insert(self.value_position, new_input)
self.value_position += 1
#Command that clears everything in the calculator's entrybox.
def clear_screen(self):
self.text_box.delete(0, END)
#Creates a message-box popup to display relevant author information.
def show_author_button(self):
messagebox.showinfo("Author", "Devin, August 2018")
#If the eval function returns a syntaxerror or a zerodivision error this command is called.
#Makes an error message popup box.
def error_popup(self):
messagebox.showwarning("Error", "Please edit your code and calculate again. Common errors include dividing by 0 and including too many decimal points in one number.")
#Uses the eval function to calculate entered string in calculator.
def calculate_answer(self):
errormessage = "SyntaxError"
try:
#Removes leading zeroes from the start of digits but not after a decimal point.
self.answer = eval(re.sub(r"((?<=^)|(?<=[^\.\d]))0+(\d+)", r"\1\2", self.equation.get()))
except (SyntaxError, ZeroDivisionError):
self.error_popup()
self.answer = eval(re.sub(r"((?<=^)|(?<=[^\.\d]))0+(\d+)", r"\1\2", self.equation.get()))
#Appends answer to list of values able to be inserted into calculator entry.
self.accepted_values.append(str(self.answer))
self.text_box.delete(0, END)
self.update_entry_with_answer()
def update_entry_with_answer(self):
self.text_box.insert(0, self.answer)
#Removes the last character in the entry field.
def backspace(self):
current = str(self.text_box.get())
new_input = current[:-1]
self.accepted_values.append(new_input)
self.text_box.delete(0, END)
self.text_box.insert(0, new_input)
#Checks for valid input
def testVal(self, entered_value, modifytype):
#Checks if text wanting to be inserted into the entryfield is valid.
if modifytype == '1': #insert
operators_and_d_point = "/*+-"
current_string = str(self.equation.get())
if entered_value == ".":
if current_string[-1] == ".":
return False
#If the last character entered was an operator, don't allow another operator or decimal point to be added to the entry box.
if entered_value in operators_and_d_point:
if current_string == "":
if entered_value == "-" or entered_value == "+":
return True
else:
return False
if current_string[-1] in operators_and_d_point:
if entered_value == "+" or entered_value == "-":
return True
else:
return False
if entered_value in self.accepted_values:
return True
#Accepts all attempts to remove text from the entryfield.
elif modifytype == "0":#delete
return True
return False
#Class to create widgets for the calculator GUI.
class CalculatorGUI(CalculatorFunctions):
def __init__(self, root):
self.root = root
self.value_position = 0
self.create_calculator_widgets()
root.bind("=", lambda event: self.calculate_answer())
self.accepted_values = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "+", "-", "*", "/", "."]
#Method called to create widgets associated with the calculator.
def create_calculator_widgets(self):
self.create_text_box()
self.create_number_buttons()
self.create_clear_button()
self.create_author_button()
self.create_backspace_button()
#Creates entry field, contents of the entry field stored as textvariable.
def create_text_box(self):
self.equation = StringVar()
self.text_box = Entry(root, justify=RIGHT, validate="key", textvariable=self.equation, font=("Helveitca", 16), borderwidth=15)
#Uses the tkinter entry box's "validatecommand" to check for valid input, method found in above class.
self.text_box['validatecommand'] = (self.text_box.register(self.testVal),'%S','%d')
self.text_box.grid(row=0, column=0, columnspan=4, ipady=10, sticky="WE")
#Method that creates calculator buttons, appends them to a list and packs them into the grid.
def create_number_buttons(self):
button_characters = "789*456/123-0.=+"
i = 0
self.button_list = []
for row_counter in range(2,6):
for column_counter in range(4):
self.button_list.append(Button(root, bg="#11708e", fg="white", activebackground="#11708e", pady=25, padx=35, text=button_characters[i], font=("Helvetica", 16, 'bold')))
self.button_list[i].grid(row=row_counter, column=column_counter, sticky="NSEW")
self.button_list[i].configure(command = lambda c=button_characters[i]: self.num_press(c))
i += 1
#Reconfigures the specific buttons.
self.button_list[3].configure(bg="#d14302", activebackground="#d14302")
self.button_list[7].configure(bg="#d14302", activebackground="#d14302")
self.button_list[11].configure(bg="#d14302", activebackground="#d14302")
self.button_list[13].configure(bg="#d14302", activebackground="#d14302")
self.button_list[14].configure(command=lambda: self.calculate_answer(), bg="#d14302", activebackground="#d14302")
self.button_list[15].configure(bg="#d14302", activebackground="#d14302")
def create_clear_button(self):
clear_button = Button(root, bg="#302e2e", fg="white", text="AC", font=("Helvetica", 12, 'bold'), pady=10, command=lambda: self.clear_screen())
clear_button.grid(row=1, columnspan=2, sticky="WE")
def create_backspace_button(self):
backspace_button = Button(root, bg="#302e2e", fg="white", text="Backspace", font=("Helvetica", 12, 'bold'), command=lambda: self.backspace())
backspace_button.grid(row=1, column=3, sticky="NSEW")
def create_author_button(self):
author_button = Button(root, bg="#302e2e", fg="white", font=("Helvetica", 12, 'bold'), text="Info", command=lambda: self.show_author_button())
author_button.grid(row=1, column=2, sticky="NSEW")
if __name__ == "__main__":
root = Tk()
calc = CalculatorGUI(root)
root.title("Calculator")
#Ensures the GUI window containing the calculator is unable to be resized, the 0,0 represents the x,y of resize allowed.
root.resizable(0, 0)
root.mainloop()
If you know you want to configure a group of widgets to an identical style save the
widget references in a dedicated object(list or dictionary).
You can then loop through those specific references applying the identical configuration.
For example if you wanted to apply red to only the operator buttons.
import tkinter as tk
master = tk.Tk()
numberframe = tk.Frame()
numberframe.grid(row=0, column=0)
button = {}
names = ('one', 'two', 'three', 'four', 'five',
'six', 'seven', 'eight', 'nine', 'zero')
for num, name in enumerate(names):
temp = tk.Button(master=numberframe,
text=(num+1)%10)
temp.grid(column=num%3,
row=num//3,
sticky='nsew')
button[name] = temp
operators = {}
operator_frame = tk.Frame()
operator_frame.grid(row=0, column=1)
operation = (('divide', '/'), ('multiply', '*'),
('subtract', '-'), ('add', '+'))
for name, sign in operation:
temp = tk.Button(master=operator_frame,
text=sign)
temp.grid()
operators[name] = temp
# example individually configure by the reference name.
button['seven'].configure(fg='red')
# example configure by group
for item in operators:
operators[item].configure(fg='red')
master.mainloop()

Using Python to update labels Tkinter

I am in the process of making a Hangman type game. So far, I have written the CLI version which works well, i'm just porting it over to create a GUI.
I have become stuck :-(. The program is not complete, and there's still more to do but I have two issues. The first is the label update.
When a letter is chosen, it creates enough dashes for the letter, and places this in a label called 'letter'.
When a user enters a letter, it replaces the dashes, however it then adds a new label next to the old label, instead, I would like to replace this label. I have tried using the .set() but this doesn't seem to work.
My second issue, and this is more of a logic error (I think), is that I wanted to keep track of the letters entered so that I could compare this to newly entered letters and alert the user. This works well, however when a letter has been entered it will warn the user even if its the first time it has been typed.
Here's the code:
import tkinter
from tkinter import *
from tkinter import messagebox
import random
guesses = 8
def play():
print("play game")
wordList = ["talking", "dollar","choice", "famous", "define", "features"]
wordChoice = random.choice(wordList)
print(wordChoice)
wordLength = (len(wordChoice))
print(wordLength)
guessedLetters = []
dashes = []
def enterLetter():
print("Guess")
global guesses
print(guessedLetters)
while guesses != 0:
guess = entry.get().lower()
if len(guess) >1:
messagebox.showinfo("Error","Sorry, only one letter at a time")
entry.delete("0","end")
return
elif guess.isalpha() == False:
messagebox.showinfo("Error","Letters only please")
entry.delete("0","end")
return
elif guess in guessedLetters:
messagebox.showinfo("Error","You have already used the letter")
entry.delete("0","end")
return
guessedLetters.append(guess)
print(guessedLetters)
print(guesses)
count = 0
for i in range(wordLength):
if wordChoice[i] == guess:
dashes[i] = guess
count = count +1
letter = Label(play, text = dashes, font = ("Arial",20)).grid(row = 2, column = i+1,padx = 10, pady =10)
if count == 0:
guesses -= 1
if guesses == 0:
print("You have ran out of guesses!")
print("The word was:",wordChoice)
###### Play Game GUI
play = Toplevel()
play.title("Play Hangman!")
label = Label(play, text="HANGMAN", font = ("Arial",16)).grid(row = 0)
label = Label(play, text="Enter your guess >").grid(row = 3, column = 0)
for i in range(wordLength):
dashes.append("-")
letter = Label(play, text = dashes, font = ("Arial",20)).grid(row = 2, column = i+1,padx = 10, pady =10)
entry = Entry(play)
entry.grid(row = 3, column = 1, columnspan = wordLength)
enterButton = Button(play, text = "Enter Guess", width = 15, command = enterLetter).grid(row = 3, column = (wordLength+2))
label = Label(play, text = "Letter used: ").grid(row = 4, columnspan = 2)
label = Label(play, text = "").grid(row= 4, columnspan = 6)
def scores():
print("check scores")
def howToPlay():
print("how to play")
####### Main Menu
root = Tk()
root.geometry("500x300")
root.title("HANGMAN")
label = Label(root, text="HANGMAN", font = ("Arial",30)).grid(row = 0, columnspan = 3)
label = Label(root, text = "Option 1 :", font = ("Arial",12)).grid(row = 1, column = 1)
playButton = Button(root, text = "Play Game", width = 15, command = play).grid(row = 1, column = 2)
label = Label(root, text = "Option 2 :", font = ("Arial",12)).grid(row = 2, column = 1)
instructionsButton = Button(root, text = "How to play", width = 15, command = howToPlay).grid(row = 2, column = 2)
label = Label(root, text = "Option 3 :", font = ("Arial",12)).grid(row = 3, column = 1)
scoresButton = Button(root, text = "View Scores", width = 15, command = scores).grid(row = 3, column = 2)
label = Label(root, text = "Option 4 :", font = ("Arial",12)).grid(row = 4, column = 1)
exitButton = Button(root, text = "Exit", width = 15, command = exit).grid(row = 4, column = 2)
root.mainloop()
You need to configure the Label, not recreate it.
Why do you use a while-loop in enter_letter? Its just run when the Button is clicked, it needs to be an if guesses > 0:
Your program did not terminate when the right word was entered; I added this.
Code:
import tkinter
from tkinter import *
from tkinter import messagebox
import random
guesses = 8
letter = None
def play():
global letter
print("play game")
wordList = ["talking", "dollar","choice", "famous", "define", "features"]
wordChoice = random.choice(wordList)
print(wordChoice)
wordLength = (len(wordChoice))
print(wordLength)
guessedLetters = []
dashes = []
play = Toplevel()
play.title("Play Hangman!")
label = Label(play, text="HANGMAN", font = ("Arial",16)).grid(row = 0)
label = Label(play, text="Enter your guess >").grid(row = 3, column = 0)
for i in range(wordLength):
dashes.append("-")
letter = Label(play, text = dashes, font = ("Arial",20))
letter.grid(row = 2, column = i+1,padx = 10, pady =10)
print(letter)
def enterLetter():
print("Guess")
global guesses, letter
print(guessedLetters)
if guesses != 0:
guess = entry.get().lower()
if len(guess) >1:
messagebox.showinfo("Error","Sorry, only one letter at a time")
return
elif guess.isalpha() == False:
messagebox.showinfo("Error","Letters only please")
return
elif guess in guessedLetters:
messagebox.showinfo("Error","You have already used the letter")
return
entry.delete("0","end")
guessedLetters.append(guess)
#print(guessedLetters)
#print(guesses)
print(dashes)
count = 0
for i in range(wordLength):
if wordChoice[i] == guess:
dashes[i] = guess
count += 1
letter.configure(text = dashes)
if count == 0:
guesses -= 1
if "".join(dashes) == wordChoice:
print("succsess!")
play.destroy()
return
if guesses == 0:
print("You have ran out of guesses!")
print("The word was:",wordChoice)
###### Play Game GUI
entry = Entry(play)
entry.grid(row = 3, column = 1, columnspan = wordLength)
enterButton = Button(play, text = "Enter Guess", width = 15, command = enterLetter).grid(row = 3, column = (wordLength+2))
label = Label(play, text = "Letter used: ").grid(row = 4, columnspan = 2)
label = Label(play, text = "").grid(row= 4, columnspan = 6)
def scores():
print("check scores")
def howToPlay():
print("how to play")
####### Main Menu
root = Tk()
root.geometry("500x300")
root.title("HANGMAN")
label = Label(root, text="HANGMAN", font = ("Arial",30)).grid(row = 0, columnspan = 3)
label = Label(root, text = "Option 1 :", font = ("Arial",12)).grid(row = 1, column = 1)
playButton = Button(root, text = "Play Game", width = 15, command = play).grid(row = 1, column = 2)
label = Label(root, text = "Option 2 :", font = ("Arial",12)).grid(row = 2, column = 1)
instructionsButton = Button(root, text = "How to play", width = 15, command = howToPlay).grid(row = 2, column = 2)
label = Label(root, text = "Option 3 :", font = ("Arial",12)).grid(row = 3, column = 1)
scoresButton = Button(root, text = "View Scores", width = 15, command = scores).grid(row = 3, column = 2)
label = Label(root, text = "Option 4 :", font = ("Arial",12)).grid(row = 4, column = 1)
exitButton = Button(root, text = "Exit", width = 15, command = exit).grid(row = 4, column = 2)
root.mainloop()
I hope this helps you.
The reason you cant update your label is because you haven't stored it in any variable. The grid, pack and place functions of the Label object and of all other widgets returns None, therefore when you call:
letter = Label(play, text = dashes, font = ("Arial",20)).grid(row = 2, column = i+1,padx = 10, pady =10)
your label cannot be accessed by variable letter. To fix this you should split it like so:
letter = Label(play, text = dashes, font = ("Arial",20))
letter.grid(row = 2, column = i+1,padx = 10, pady =10)
To update text of that label, you can call .configure(text = 'new text') on it.
# letter = Label(play, text = dashes, font = ("Arial",20)).grid(row = 2, column = i+1,padx = 10, pady =10) #
letter.configure(text = dashes)
As for your second issue, i think you confused while loop and if statement in the function enterLetter. It's called once per click and you only need to check one time if player has ran out of guesses.
if guesses != 0:
...
elif guesses == 0:
....

Categories

Resources