Tkinter button.config() if statements - python

I have buttons containing images which once clicked will change the image in the button.
If the image is an empty star then the button will update to a full star once clicked and again once clicked this option will revert back to the empty star image in the button.
I tried:
firststar = partial(starsystemfeed, 1)
star_1 = Button(framefeed, image=emptystar, compound="right", command=firststar)
emptystar = PhotoImage(file='icons/emptystar.png')
fullstar = PhotoImage(file='icons/fullstar.png')
def starsystemfeed(num):
global star_1, star_2, star_3, star_4, star_5
global fullstar
global emptystar
if num == 1:
if star_1.config(image = emptystar) is True:
print("1")
star_1.config(image=fullstar)
else:
print("2")
star_1.config(image=emptystar)
but this doesn't seem to work as I keep on getting option "2" on the emptystar image suggesting that I'm doing it wrong

Try this:
firststar = partial(starsystemfeed, 1)
star_1 = Button(framefeed, image=emptystar, compound="right", command=firststar)
emptystar = PhotoImage(file='icons/emptystar.png')
fullstar = PhotoImage(file='icons/fullstar.png')
def starsystemfeed(num):
global star_1, star_2, star_3, star_4, star_5
global fullstar
global emptystar
if num == 1:
if str(star_1.cget("image")) == str(emptystar):
print("1")
star_1.config(image=fullstar)
else:
print("2")
star_1.config(image=emptystar)
You can use <tkinter Widget>.cget("<attribute name>") to get the attribute back from the widget. In this case we want back the image so we use star_1.cget("image") and we check if that is the same as emptystar.

Related

Time.sleep() not pausing the correct moment?

Hello I have created a quiz using python and tkinter. After each option is pressed I wanted the correct answer to turn green and the three incorrect to turn red then revert to the default for the next question.The problem here being that running the code will take the buttons to the default before the user can see the colours. To do this I tried to use time.sleep() in the function however no matter where I use it it just seems to pause on the button being pressed down and then goes onto the next question without seeing any colour change.
Here is the relevant piece of code
def entry(num):
global score
global x
global count
count +=1
if Qa[x] == 1:
option1.config(bg = "green")
option2.config(bg = "red")
option3.config(bg="red")
option4.config(bg="red")
elif Qa[x] == 2:
option1.config(bg="red")
option2.config(bg="green")
option3.config(bg="red")
option4.config(bg="red")
elif Qa[x] == 3:
option1.config(bg="red")
option2.config(bg="red")
option3.config(bg="green")
option4.config(bg="red")
elif Qa[x] == 4:
option1.config(bg="red")
option2.config(bg="red")
option3.config(bg="red")
option4.config(bg="green")
if num == Qa[x]:
score += 1
x +=1
if count <10:
my_label.config(text = Qs[x])
option1.config(text = (question_prompts[x])[1],bg = "SystemButtonFace",command = lambda: entry(1) )
option2.config(text=(question_prompts[x])[2],bg = "SystemButtonFace",command = lambda: entry(2) )
option3.config(text=(question_prompts[x])[3],bg = "SystemButtonFace",command = lambda: entry(3) )
option4.config(text=(question_prompts[x])[4],bg = "SystemButtonFace",command = lambda: entry(4) )
else:
End_score =Label(text = "Well done you scored" +" "+ str(score)+" " +"out of 11", font = 40)
End_score.place(relx=0.5,rely =0.5,anchor = CENTER)
print(x,score, count, Qa[x])
I haven't put the time.sleep() in here because I have tried it everywhere in this section an it gives the same result
I would really appreciate some help
The PROBLEM here is that the options will not actually change color until Tk can get back to its main loop. As long as you are running your function, the main loop cannot pull new events. You need to set the colors, then use root.after to schedule a callback at some point in the future where you reset to all green.

Pause a for loop to wait for a button press

I am trying to build an image classifier and I need to manually assign classifications for a training dataset, so I build a file browser /media window app in tkinter to display images and assign them classifications via a button click. To iterate over the files, I am using a for loop, but I need it to pause and wait for that input. Here is my code:
def setKnownData(self):
training_sample = self.dataset.sample(frac = .1)
print(training_sample)
for i, row in training_sample.iterrows():
print(row)
global img
file = Image.open(row['ID'])
resized = file.resize((500,600))
img = ImageTk.PhotoImage(resized)
self.media_window.create_image(0,0, anchor = 'nw', image = img)
self.event_var.get()
while True:
if self.event_var.get() == 0:
print(self.event_var.get())
return
if self.event_var.get() == 1:
training_sample.loc[row]['class'] = 'cartoon'
break
elif self.event_var.get() ==2:
training_sample.loc[row]['class'] = 'photo'
break
self.event_var.set(0)
def stateSwitch(self, action):
print('state switching....')
if action == 'toon':
print(self.event_var.get())
self.event_var.set(1)
print('classification: TOON', self.event_var.get())
elif action == 'photo':
self.event_var.set(2)
print('classification: PHOTO')
I've exausted every combination of IntVar, tkinter, and for loop searches and can't find a viable solution, so I apologize if this is a repeat question. How can I pause this for loop, wait for a putton press, and then proceed to the next image in the list?
You need to shift your thinking away from pausing the loop. That's procedural programming, but GUIs are work much better as "event driven programming", where the entire program is just endlessly waiting for an event (like a button press) to happen. The means no loops, besides the tkinter mainloop. And it means making a new function for every event.
def setKnownData(self):
training_sample = self.dataset.sample(frac = .1)
print(training_sample)
self.training_sample = training_sample.iterrows()
def on_button_click(self):
i, row = next(self.training_sample)
print(row)
global img
file = Image.open(row['ID'])
resized = file.resize((500,600))
img = ImageTk.PhotoImage(resized)
self.media_window.create_image(0,0, anchor = 'nw', image = img)
self.event_var.get()
while True:
if self.event_var.get() == 0:
print(self.event_var.get())
return
if self.event_var.get() == 1:
training_sample.loc[row]['class'] = 'cartoon'
break
elif self.event_var.get() ==2:
training_sample.loc[row]['class'] = 'photo'
break
self.event_var.set(0)

How to play an audio when a button is pressed

I am having problems with setting up a button that will play audio when pressed.
I am using tkinter for the button and simpleaudio for the audio.
x = ""
def audio(file):
x = file
return x
window = tk.Tk()
window.title("Audio")
audio_button = tk.Button(window,text="Audio Test",command=audio("imposter")).pack(side="left")
audio_button1 = tk.Button(window,text="Audio Test Two",command=audio("crewmate")).pack(side="left")
stop_all = tk.Button(window,text="Stop All",command=audio("stop"),fg="red").pack(side="left")
tk.mainloop()
while True:
if not x == "stop":
if x == "crewmate":
wave_obj = sa.WaveObject.from_wave_file("File here")
play_obj = wave_obj.play()
while play_obj.is_playing():
if x == "stop":
break
else:
pass
elif x == "imposter":
wave_obj = sa.WaveObject.from_wave_file("File here")
play_obj = wave_obj.play()
while play_obj.is_playing():
if x == "stop":
break
else:
pass
else:
pass
And it would be nice to help shorten the code a bit too
pack returns None, therefore you should chain pack to the creation of a widget when you need to keep a reference to the widget.
Here is a possible approach to your problem; I do not have simple audio, so I cannot guarantee the code is fully correct; the code lines with simpleaudio calls are commented out, and print statements allow you to verify the logic. You will have to un-comment them, and try.
The logic stops all audio before launching a new file; you may want to make it a bit more user friendly, like letting the song finish if the command requests the same song, instead of restarting it.
# import simpleaudio as sa
import tkinter as tk
def stop_audio():
print('stopping all')
# sa.stopall()
def test_audio(state):
if state == 'imposter':
path = "path/to/imposter.wav"
elif state == 'crewmate':
path = "path/to/crewmate.wav"
else:
return
# wave_obj = sa.WaveObject.from_wave_file(path)
stop_audio()
# play_obj = wave_obj.play()
print(f'playing {state}')
window = tk.Tk()
window.title("Audio")
audio_button = tk.Button(window,text="Audio Test", command=lambda state='imposter': test_audio(state))
audio_button.pack(side="left")
audio_button1 = tk.Button(window,text="Audio Test Two", command=lambda state='crewmate': test_audio(state))
audio_button1.pack(side="left")
stop_all = tk.Button(window,text="Stop All",fg="red", command=stop_audio)
stop_all.pack(side="left")
tk.mainloop()

How to stay in while loop, but pausing to receive new button input

I'm new at programming and I cant seem to figure out how to make "konto" keep updating without it being in a while loop. Problem with the while loop is it doesn't allow new button input because it freezes the window.
I've tried making "konto" local, splitting the code into different functions, changing the loop style. Break exits the loop so "konto" doesn't update.
konto = 100
roulette_window = Tk()
def roulette(chosen_color, sats, konto):
while True:
x = randint(0, 36)
if x == 0:
green_num = x
print(green_num, 'Green')
color = ('Green')
if color and chosen_color == 'Green':
win_amount = 35 * int(sats)
elif (x % 2) == 0:
red_num = x
print(red_num, 'Red')
color = ('Red')
if color and chosen_color == 'Red':
win_amount = 2 * int(sats)
elif (x % 2) == 1:
black_num = x
color = ('Black')
print(black_num, 'Black')
if color and chosen_color == 'Black':
win_amount = 2 * int(sats)
if not color == chosen_color:
win_amount = 0
konto = konto - int(sats) + int(win_amount)
print(konto)
def bet_black():
sats = bet_input.get(1.0, END)
chosen_color = 'Black'
bet_input.delete(1.0, END)
roulette(chosen_color, sats, konto)
def bet_red():
I want to be able to call the function "roulette" once by eg. bet_black button click, and then click bet_red after one loop, while still having the updated "konto" variable.
Firstly your variable konto is a global variable and you need to indicate to python that it is. Change your method to:
def roulette(chosen_color, sats):
global konto
...
Variables in Python have scoping rules which determine where they are valid. Anytime you assign to a variable a local version of the variable is created unless you have explicitly marked it as a global variable.
Secondly, I'm not sure why you have the while True structure here. This will cause an infinite loop unless you break out of it or return.

Command function for button "resets"

So in my tkinter python program I am calling on a command when a button is clicked. When that happens it runs a function but in the function I have it set a label to something on the first time the button is clicked and after that it should only update the said label. Basically after the attempt it changes the attempt to 1 ensuring the if statement will see that and not allow it to pass. However it keeps resetting and I don't know how to stop it. When you click the button no matter first or third the button resets and proof of that occurs because the h gets printed. It's as if the function restarts but it shouldn't since it's a loop for the GUI.
def fight(): #Sees which one is stronger if user is stronger he gets win if no he gets loss also displays enemy stats and removes used characters after round is finished
try:
attempt=0
namel = ""
namer=""
left = lbox.curselection()[0]
right = rbox.curselection()[0]
totalleft = 0
totalright = 0
if left == 0:
namel = "Rash"
totalleft = Rash.total
elif left==1:
namel = "Untss"
totalleft = Untss.total
elif left==2:
namel = "Illora"
totalleft = 60+35+80
if right == 0:
namer = "Zys"
totalright = Zys.total
elif right==1:
namer = "Eentha"
totalright = Eentha.total
elif right==2:
namer = "Dant"
totalright = Dant.total
lbox.delete(lbox.curselection()[0])
rbox.delete(rbox.curselection()[0])
print(namel)
print(namer)
if attempt == 0:
wins.set("Wins")
loss.set("Loss")
print("h")
attempt=1
if (totalleft>totalright):
wins.set(wins.get()+"\n"+namel)
loss.set(loss.get()+"\n"+namer)
else:
wins.set(wins.get()+"\n"+namer)
loss.set(loss.get()+"\n"+namel)
except IndexError:
pass
Also for those of you who saw my previous question I still need help with that I just also want to fix this bug too.
At beginning of function fight you set attempt = 0 so you reset it.
Besides attempt is local variable. It is created when you execute function fight and it is deleted when you leave function fight. You have to use global variable (or global IntVar)
attempt = 0
def fight():
global attempt
BTW: of you use only values 0/1 in attempt then you can use True/False.
attempt = False
def fight():
global attempt
...
if not attempt:
attempt = True

Categories