Python 3 Tkinter How to Refresh/Update Labels? Nim Game - python

I am unable to update the variables inside the labels even with the use of IntVar(). I am probably doing something chronically wrong. Tried to make a Nim game that updates the labels via changing the variables.
# ministry of trade
from tkinter import *
import random
# create root window
root = Tk()
# ministry of variables
lst_choice = [1,2]
lst_player = [1,2]
player=random.choice(lst_player) #set random player
playervar = StringVar()
playervar.set(player)
# Set the initial state
state=7 # number of objects
statevar = IntVar()
statevar.set(state)
winner=0
winnervar = IntVar()
winnervar.set(winner)
move=0
movevar = IntVar()
movevar.set(move)
def move1():
# move is assigned
global statevar
global playervar
global winnervar
if statevar.get()>0:
statevar.set(statevar.get() - 1)
print(statevar.get()) #for testing only
return statevar
if statevar.get()==0: # check win status - win, lose, stalemate
winnervar.set(playervar.get())
print(winnervar.get()) #for testing only
return winnervar
if playervar.get()==1: # switch players 2->1, 1->2 go back to the valid move line
playervar.set(2)
print(playervar.get()) #for testing only
return playervar
else:
playervar.set(1)
print(playervar.get()) #for testing only
return playervar
root.update_idletasks()
def move2():
return
# Holy Roman Empire of the Widget Nation
# electorate of Label
labelAantalMunten = Label(root, text=("Aantal munten op stapel ",statevar.get())) #state
labelAantalMunten.pack() # orders to add the widget label to the root window and display it
labelWelkSpeler = Label(root, text=("Nu is speler ",playervar.get()," aan de beurt")) #player
labelWelkSpeler.pack()
labelWelkWinaar = Label(root, text=("Speler ",winnervar.get()," heeft gewonnen!")) #winner
labelWelkWinaar.pack()
# electorate of Button
button1 = Button(root, text="neem 1 steen", command=move1)
button1.pack()
button2 = Button(root, text="neem 2 stenen", command=move2)
button2.pack()
# start the main events loop
root.mainloop()

Below is an example of what you should do to get the label display updated after every press of your button.
You need to use textvariable instead of text in label.
You need to declare a StringVar variable in your main code and have it updated in your function mov1.
Note my example on how to get a string before passing it into your
StingVar variable.
Have fun!
Example code
# ministry of trade
from tkinter import *
import random
# create root window
root = Tk()
# Set the initial state
state=7 # number of objects
statevar = IntVar()
statevar.set(state)
def move1():
# move is assigned
global statevar
if statevar.get()>0:
print('statevar.get()>0')
print(statevar.get()) #for testing only
statevar.set(statevar.get() - 1)
print(statevar.get()) #for testing only
a = "Aantal munten op stapel " + str(statevar.get())
print('a = {}'.format(a))
text1.set(a)
return statevar
a = "Aantal munten op stapel " + str(statevar.get())
text1=StringVar()
text1.set(a)
labelAantalMunten = Label(root, textvariable=text1) #state
labelAantalMunten.pack() # orders to add the widget label to the root window and display it
# electorate of Button
button1 = Button(root, text="neem 1 steen", command=move1)
button1.pack()
# start the main events loop
root.mainloop()

labelWelkSpeler = Label(root, text=("Nu is speler ",playervar.get()," aan de beurt")) #player
This gets the value of playervar ONCE (at the time you create the label), and builds the text of the label at that time. (And probably doesn't build the text you actually wanted, as what you wrote is a tuple construction, not a string concatenation.) It does not magically cause this code to be rerun every time that playervar changes!
The only way to get a Label to auto-update from a variable is to specify it as the textvariable= option, not text=. The variable has to hold the entire text to be shown, so it's almost certainly going to be a StringVar, not an IntVar. You would need to keep the player score in a separate variable (an ordinary Python variable, most likely), then .set() the StringVar yourself whenever it changes.

Related

Global Variable Undefined? Can't get label to display global variable from function

This is a little program I am making to learn Python which is a NPC Trait Generator that generates random words from a text file.
My random generator works, but I can't get it to display properly in the label. I currently have pertrait set to global but it doesn't seem to pick up the variable to display in the label? I tried setting up StringVar but I couldn't seem to get that to work properly either.
import tkinter as tk
from tkinter import font
# Random Personality generation from text list
def gen():
global pertrait
pertrait = print(random.choice(open(
'F:\\Desktop\\python\\RandomGenerator py\\CharTraitList.txt').read(
).split()).strip())
root = tk.Tk()
root.title("NPC Trait Generator")
root.geometry("500x200")
frame = tk.Frame(root)
frame.pack()
# define font
fontStyle = font.Font(family='Courier', size=44, weight='bold')
# background image variable and import
paper = tk.PhotoImage(
file='F:\\Desktop\\python\\RandomGenerator py\\papbckgrd.png')
butQuit = tk.Button(frame,
text="Quit",
fg="red",
command=quit)
butQuit.pack(side=tk.LEFT)
ButGen = tk.Button(frame,
text="Generate",
command=gen)
ButGen.pack(side=tk.RIGHT)
# Label generation
Trait1 = tk.Label(root,
compound=tk.CENTER,
text=pertrait,
font=fontStyle)
# image=paper) .pack(side="right")
Trait1.pack()
root.mainloop()
print() doesn’t return a value, and you neglected to call gen()!
Change:
pertrait = print(random.choice(open(
'F:\\Desktop\\python\\RandomGenerator py\\CharTraitList.txt').read(
).split()).strip())
To:
pertrait = random.choice(open(
'F:\\Desktop\\python\\RandomGenerator py\\CharTraitList.txt').read(
).split()).strip()
But really it would be much better t avoid the global - make gen() return the value and assign the result of gen() ot pertrait

How to set checkbuttons to display a word in a label when enough checkbuttons are clicked?

I'm trying to make checkbuttons display a sentence on a label when three of the checkbuttons are clicked.
New to tkinter, here's what I have tried so far.
from tkinter import *
root = Tk()
Check_button_one = IntVar()
Check_button_two = IntVar()
Check_button_three = IntVar()
Checkbutton(root, variable = Check_button_one).pack()
Checkbutton(root, variable = Check_button_two).pack()
Checkbutton(root, variable = Check_button_three).pack()
default_text = ""
updated_text = "what a legend you are!"
while Check_button_one == 1 and Check_button_two == 1 and Check_button_three == 1:
default_text = updated_text
Label_main = Label(root, text = default_text)
Label_main.pack()
Although there is a lot of good advice in #martineau's answer (mainloop(), how to change the label's text), I think the polling approach is not suitable for a GUI application.
Assuming that the CheckButtons are (de)activated by the user (via mouse/keyboard)*, you do not want to regularly check their states. When polling in intervals, you have two problems:
No immediate response to user action: in the worst case, the label's text will only be updated after the interval, i.e. if the interval is 500ms, the label could change 500ms after the checkbutton was clicked.
Most of the time, your code does unnecessarily check the button states although they haven't changed
The correct way to handle user interaction is specifying a callback that is executed whenever the CheckButton's state has changed as a result of a user action. CheckButton takes a command parameter for that purpose:
from tkinter import *
root = Tk()
Check_button_one = IntVar()
Check_button_two = IntVar()
Check_button_three = IntVar()
default_text = ""
updated_text = "what a legend you are!"
Label_main = Label(root, text = default_text)
Label_main.pack()
def checkbuttonCallback():
if Check_button_one.get() and Check_button_two.get() and Check_button_three.get():
Label_main.config(text=updated_text)
else:
Label_main.config(text=default_text)
Checkbutton(root, variable = Check_button_one, command=checkbuttonCallback).pack()
Checkbutton(root, variable = Check_button_two, command=checkbuttonCallback).pack()
Checkbutton(root, variable = Check_button_three, command=checkbuttonCallback).pack()
root.mainloop()
* If you change the CheckButtons' states via code, you can simply check the states after you changed them.
You need to periodically check the status of the Buttons while the GUI mainloop() is running. You can do that in a tkinter app by using the universal widget after method.
For example:
from tkinter import *
root = Tk()
Check_button_one = IntVar()
Check_button_two = IntVar()
Check_button_three = IntVar()
Checkbutton(root, variable = Check_button_one).pack()
Checkbutton(root, variable = Check_button_two).pack()
Checkbutton(root, variable = Check_button_three).pack()
default_text = ""
updated_text = "what a legend you are!"
Label_main = Label(root, text=default_text)
Label_main.pack()
def check_buttons():
if Check_button_one.get() and Check_button_two.get() and Check_button_three.get():
Label_main.config(text=updated_text)
else: # Make sure the default is displayed.
Label_main.config(text=default_text)
root.after(500, check_buttons) # Schedule next check in 500 msecs
check_buttons() # Start polling buttons.
root.mainloop()

How to add a reset button that clears my code?

I'm making a basic TicTacToe UI in python, and I believe that a fundamental item to this code is a reset button which resets your codes back to the default. is there any other way to do this?
I've Tried to define a function which resets the text of the button back to " " but I don't think that's a great idea because of a lot of other complexities within the cod.
from tkinter import *
root = Tk()
def changetext():
BTN1["text"] = "X"
BTN1 = Button(root, text=" ", command=changetext)
BTN1.pack()
root.mainloop()
So I want to add a button here that says "Reset Text" and it resets all the codes to defaults.
The easiest way to reset the game would be to
Reset the UI as you suggest, with a single dedicated reset_UI() function
Reset the board state by creating a new game board object, and discarding the old one
This of course means that you'll need to wrap all your variables and functions in a board class Board, so that there aren't a billion global variables you have to worry about resetting. The only thing that should persist between resets are your UI buttons, which can be created in your main() function before initializing the game board.
Here's code demonstrating how something like that could be done (plus a few other things):
import tkinter as tk
def toggle(btn):
if btn["text"] == "X":
btn["text"] = " "
else:
btn["text"] = "X"
def reset(buttons):
for btn in buttons.values():
btn["text"] = " "
root = tk.Tk()
buttons = {}
for row in range(3):
for col in range(3):
button = tk.Button(root, text=" ", width=1, height=1)
button.config(command=lambda btn=button: toggle(btn))
button.grid(row=row, column=col)
buttons[row, col] = button
reset_button = tk.Button(root, text="Reset", command=lambda: reset(buttons))
reset_button.grid(columnspan=3)
root.mainloop()

Change label text on button using values from tuple click tkinter

I have a list of strings sorted in a tuple like this:
values = ('1.Python','2.Ruby','3.PHP','4.Perl','5.JavaScript')
My simple code is:
from tkinter import *
root = Tk()
values = ('1.Python','2.Ruby','3.PHP','4.Perl','5.JavaScript')
ru = Button(root,
text="Next",
)
ru.grid(column=0,row=0)
lab = Label(root,
text=values[0])
lab.grid(column=1,row=0)
ru2 = Button(root,
text="Previous"
)
ru2.grid(column=2,row=0)
root.mainloop()
I have two tkinter buttons "next" and "previous", the text value of the Label is directly taken from the tuple (text=value[0]), however I would want to know how to show the next string from the tuple when the next button is pressed, and how to change it to the previous values when the "previous" button is pressed. I know it can be done using for-loop but I cannot figure out how to implement that. I am new to python.
Use Button(..., command=callback) to assign function which will change text in label lab["text"] = "new text"
callback means function name without ()
You will have to use global inside function to inform function to assign current += 1 to external variable, not search local one.
import tkinter as tk
# --- functions ---
def set_next():
global current
if current < len(values)-1:
current += 1
lab["text"] = values[current]
def set_prev():
global current
if current > 0:
current -= 1
lab["text"] = values[current]
# --- main ---
values = ('1.Python','2.Ruby','3.PHP','4.Perl','5.JavaScript')
current = 0
root = tk.Tk()
ru = tk.Button(root, text="Next", command=set_next)
ru.grid(column=0, row=0)
lab = tk.Label(root, text=values[current])
lab.grid(column=1, row=0)
ru2 = tk.Button(root, text="Previous", command=set_prev)
ru2.grid(column=2, row=0)
root.mainloop()
BTW: if Next has to show first element after last one
def set_next():
global current
current = (current + 1) % len(values)
lab["text"] = values[current]
def set_prev():
global current
current = (current - 1) % len(values)
lab["text"] = values[current]

How to update Label widget each time Button is clicked using TkInter - Python

So I am trying to build a simple calculator that displays the number pi i.e., 3.14 in Label and each time the button 'click me' is clicked it adds a another decimal value to the 3.14. For example, once clicked the Label would display 3.141, second time: 3.1415 etc etc.
Here is the code:
# Import the Tkinter functions
from Tkinter import *
# Create a window
loan_window = Tk()
loan_window.geometry('{}x{}'.format(500, 100))
# Give the window a title
loan_window.title('Screeen!')
## create the counter
pi_num = 3.14
# NUMBER frame
frame = Frame(loan_window, width=100, height=50)
frame.pack()
def addPlaceValue():
pi_num= 3.14
return pi_num
## create a label and add it onto the frame
display_nums = Label(frame, text = 'pi_num')
display_nums.pack()
#### create a label and add it onto the frame
##display_nums = Label(frame, text = pi_num)
##display_nums.pack()
##
# Create a button which starts the calculation when pressed
b1 = Button(loan_window, text = 'Click me', command= addPlaceValue, takefocus = False)
b1.pack()
# Bind it
loan_window.bind('<Return>', addPlaceValue())
# event loop
loan_window.mainloop()
I've tried many times to keep track of the button clicks, but failed to do so. I see one issue; the code doesn't know which n-th time a button is clicked. Any ideas?
I don't know how you have pi stored for the number of digits.... But, here's one way to specify the precision very easily (let's say a precision of 100):
from sympy import mpmath
mpmath.dps = 100 #number of digits
PI = str(mpmath.pi)
The PI is a constant instance and isn't naturally subscriptable, so we convert it to a str for later indexing.
Now, as for updating the text we can keep track of a counter each time the button is placed.
We can set this counter as an attribute to loan_window with a default of 4 to display the most common representation of pi 3.14 then increment and change the label text:
Edit: When binding you also want to just pass the function name instead of function_name() this is actually calling the function
import sympy, Tkinter as tk
sympy.mpmath.dps = 100
PI = str(sympy.mpmath.pi)
loan_window = tk.Tk()
loan_window.counter = 4
frame = tk.Frame(loan_window, width=100, height=50)
frame.pack()
def addPlaceValue():
loan_window.counter += 1
display_nums['text'] = PI[:loan_window.counter]
display_nums = tk.Label(frame, text = PI[:loan_window.counter])
display_nums.pack()
b1 = tk.Button(loan_window, text = 'Click me', command= addPlaceValue, takefocus = False)
b1.pack()
loan_window.bind('<Return>', addPlaceValue)
loan_window.mainloop()

Categories