Pause a for loop to wait for a button press - python

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)

Related

How to click several image at random place in sequence from 1 to 5 using pyautogui

How to use PyAutoGui LocateOnScreen() to click on an image placed randomly on the screen?
but it have to click the image in order from 1 to 5.
Assuming that the images you have to click appear randomly anywhere on the screen, but the images are not random, all you would have to do is organize the code.
(just so you know I like to import 'pyautogui' as 'pe')
for example:
import pyautogui as pe
import time
image_1 = pe.locateCenterOnScreen('first_image_example')
image_2 = pe.locateCenterOnScreen('second_image_example')
image_3 = pe.locateCenterOnScreen('third_image_example')
image_4 = pe.locateCenterOnScreen('fourth_image_example')
image_5 = pe.locateCenterOnScreen('fifth_image_example')
waitingloop = 1
while waitingloop == 1:
if image_1 == None:
time.sleep(0.5)
else:
time.sleep(0.5)
pe.click(image_1)
break
time.sleep(0.1)
while waitingloop == 1:
if image_2 == None:
time.sleep(0.5)
else:
time.sleep(0.5)
pe.click(image_1)
break
time.sleep(0.1)
while waitingloop == 1:
if image_3 == None:
time.sleep(0.5)
else:
time.sleep(0.5)
pe.click(image_3)
break
time.sleep(0.1)
while waitingloop == 1:
if image_4 == None:
time.sleep(0.5)
else:
time.sleep(0.5)
pe.click(image_4)
break
time.sleep(0.1)
while waitingloop == 1:
if image_5 == None:
time.sleep(0.5)
else:
time.sleep(0.5)
pe.click(image_5)
break
This is a very easy way to do it, probably not the most efficient (since we have lots of lines of code), so if you want you can try to experiment with this.
So, to break this down, as you can see I first defined a 'waitingloop = 1', which we will use to create a loop that will run forever, until the process is completed correctly... So for example in the first loop (for image_1), it says that everytime the 'locateCenterOnScreen()' function returns 'None' (a.k.a. didn't find the image) it will rerun the command again, until it finds the image, and once it finds it, it will click it and after that the loop breaks, once the loop break it waits a little bit (time.sleep) and then it starts up a new loop for 'image_2' and it repeats the same process until all 5 images have been clicked.
This way all of your images will be clicked in other from 1 to 5 one at a time.
Hope I was able to help you. Also try to be very detailed when asking something.

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.

Tkinter button.config() if statements

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.

Time.Sleep Alternative? Need to be able to break loop/return to main

import time
import pyautogui
from pyautogui import *
import keyboard
from PIL import ImageGrab, Image
from PIL import *
import os
import win32api, win32con
import cv2
import pyperclip
from pynput.keyboard import Key, Listener
import pytesseract
import numpy as np
from numpy import *
from threading import Event
##1340,182, 1777, 213
test = 0
src_path = os.getcwd()
pytesseract.pytesseract.tesseract_cmd = r'C:\\Program Files\\Tesseract-OCR\\tesseract'
def get_region(box):
im = ImageGrab.grab(box)
im = im.save(os.getcwd() + "\\logs.png", 'PNG')
def get_string(img_path):
img = cv2.imread(img_path)
kernel = np.ones((1, 1), np.uint8)
img = cv2.dilate(img, kernel, iterations=1)
img = cv2.erode(img, kernel, iterations=1)
red = [0,0,255]
img[img != red] = 255
cv2.imwrite(src_path + "\\logs.png", img)
result = pytesseract.image_to_string(Image.open(src_path + "\\logs.png"))
return result
def alertf1(c):
global test
time.sleep(0.1)
while c == 1:
if keyboard.is_pressed('f1'): # if key 'f1' is pressed
print('f1 has been pressed')
c = 0
break
else:
result = get_string(src_path + "\\logs.png")
print('checking if anything is destroyed')
if result.find('destroved') != -1:
print('something got destroyed!')
pyautogui.keyDown('ctrl')
time.sleep(0.1)
pyautogui.keyDown('tab')
pyautogui.keyUp('ctrl')
pyautogui.keyUp('tab')
time.sleep(0.5)
for char in '#':
pyperclip.copy(char)
pyautogui.hotkey('ctrl', 'v', interval=0.1)
pyautogui.write('everyone Enemies/Red Logs! Something has been destroyed.')
pyautogui.press('enter')
pyautogui.click(x=1860, y=50)
else:
if keyboard.is_pressed('f1'): # if key 'f1' is pressed
print('f1 has been pressed')
c = 0
break
def on_press(key):
global test
test = test + 1
region = (1341,185, 1778, 215)
get_region(region)
if key == Key.f1 and test == 1:
print('before alert f1 pressed')
test = test + 1
time.sleep(0.1)
alertf1(1)
if key == Key.f1 and test == 3:
test = 0
with Listener(on_press=on_press) as listener:
listener.join()
def main():
while True:
print('on press going now')
on_press(key)
if __name__ == '__main__':
main()
Thats my current code, a small explanation of what it does and what I want:
Upon start, if user presses 'F1' it will take a screenshot of a small area and turn the image into text, it will then run an infinite loop, to check if the text I got from the image is = to 'destroyed' if it is then user gets notified and if it isn't it will be on a infinite loop getting new screenshots and checking it again.
I also added the "if keyboard.is_pressed('f1'):" to break the infinite loop.
The 2 issues I have right now:
1- After user gets notified I want the program to not proceed into the infinite loop for 5 minutes, time.sleep (300) works but the issue is that if the user wants to cancel the current function and presses F1 to break the loop back into Main() it will not get detected, thus I need another alternative to time.sleep because it just freezes the code completely. I heard that threading.event could work but I have honestly no idea how it works, and even after checking a couple examples I am not sure how I could make it work still.
2- I found that 'if keyboard.is_pressed('f1'):' Isn't very effective as it sometimes doesn't trigger and have to click quite a few times until it triggers and breaks loop. What would be a good alternative?

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()

Categories