I have created a simple video game BOT using openCV 4.6.0 and 64-bit python.
Program flow listed below;
1. Open GUI using tkinker
2. Upon pressing start a new process opens in a loop (main loop)
3. Program takes a screenshot of my screen (850x650 pixels) and saves as PNG
4. Load in the screenshot to openCV MAT and start image processing with cv.matchtemplate
5. Repeat main loop. After 1 hour kill this process, open a new process and repeat
The reason why I have this running on a separate process was because I thought that maybe openCV was locking memory in the background so I'm essentially doing a "Reboot".
All the OpenCV related code is below;
#Static Images
hp_img = cv.imread(LOCAL_DIRECTORY + 'hp.png', cv.COLOR_BGR2GRAY)
worldmap_img = cv.imread(LOCAL_DIRECTORY + 'worldmap.png', cv.COLOR_BGR2GRAY)
settings_menu = cv.imread(LOCAL_DIRECTORY + 'settings.png', cv.COLOR_BGR2GRAY)
black_screen = cv.imread(LOCAL_DIRECTORY + 'blackscreen.png', cv.COLOR_BGR2GRAY)
def checkBlackScreen():
global threshold
screenShot = pyautogui.screenshot(LOCAL_DIRECTORY + "screen.png", region=(0, 0, 850, 650))
screen_img = cv.imread(LOCAL_DIRECTORY + 'screen.png', cv.COLOR_BGR2GRAY)
result = cv.matchTemplate(screen_img, black_screen, cv.TM_CCOEFF_NORMED)
_, max_val, _, max_loc = cv.minMaxLoc(result)
del result
del screen_img
del screenShot
gc.collect()
if (max_val > threshold):
print("[BAD] Stuck in black screen")
return True
else:
print("[Good] Not stuck in black screen : ", max_val)
return False
The main loop just calls the function above and presses buttons accordingly. Most of the program provided below;
#Main entry of the program
if __name__ == '__main__':
GUI()
#GUI using TK
def GUI():
root = Tk()
frm = ttk.Frame(root, padding=10)
frm.grid()
ttk.Label(frm, text="Hello World!").grid(column=0, row=0)
ttk.Button(frm, text="Quit", command=root.destroy).grid(column=1, row=0)
ttk.Button(frm, text="Start", command=startBotProcess).grid(column =2, row=0)
ttk.Button(frm, text="Stop", command=stopBot).grid(column =3, row=0)
ttk.Button(frm, text="Set WorldMap XY", command=StartListenClick).grid(column =4, row=0)
#ttk.Button(frm, text="Set WorldMap XY", command=checkEXPMap).grid(column =4, row=0)
root.mainloop()
#Set world map X,Y
def on_click(x, y, button, pressed):
listen_for_mouse = False
if pressed:
global MOUSE_X
global MOUSE_Y
MOUSE_X = x
MOUSE_Y = y
print('World Map set to : ',(MOUSE_X, MOUSE_Y))
listen_for_mouse = False
if not pressed:
# Stop listener
return False
# Collect events until released
def StartListenClick():
listen_for_mouse = True
with Listener(on_click=on_click) as listener:
if (listen_for_mouse == True):
listener.join()
#Start new BOT Process
def startBotProcess():
print("[SYSTEM] New BOT Process STARTED")
time_start = time.time()
ps = Process(target=processMain, args=(MOUSE_X,MOUSE_Y))
ps.start()
ps.join()
stopBot()
print("[SYSTEM] Bot Process CLOSED")
if (time.time() - time_start > SLEEP):
print("[SYSTEM] Restart Timer Created")
t = Timer(SLEEP_TIME, botSleep)
t.start()
def processMain(x,y):
global MOUSE_X, MOUSE_Y
MOUSE_X = x
MOUSE_Y = y
startBot()
def startBot():
print("[SYSTEM] STARTING BOT - Click Window")
global running
global GAME_STATE
global CUT_SCENE
global CHAT_FOUND
global TRANSITION_TIME
printData()
time.sleep(2)
running = True
start_time = time.time()
while (running == True):
if (GAME_STATE == 0): #First, lets see if we are in a map
if (checkMap() == True):
GAME_STATE = 1
else:
print("GAME_STATE is 0 - Cannot determine if player is on a map")
debugger()
Now eventually after ~ 2hours of running the python command prompt will show a fail to allocate memory error
enter image description here
The line that its referring too is the cv.matchtemplate.
Task manager shows the program only using 50mb of memory. Total memory usage on my computer is 51%
Does anyone know what I can do to resolve this? Task manag
Updated screenshot with task manager and failed allocation;
enter image description here
Related
I want to break a loop with the same button that I started the loop with.
For all intents and purposes, it does what I need it to, up until I click the button again. If I hit the button again, it crashes. Right now with the debug visual setup, I can hit q to break the loop. But down the road I obviously want to turn that off and just have it all go through the button instead.
I should also note that the button does not update unless the window is interacted with/moved. This goes for when the button is pushed. It doesn't crash until you move the window.
def bttnup():
global is_on
#starts autoaccept
if is_on:
button.config(image = selected,
bg=selected_bg
)
start()
is_on = False
else:
#Stops autoaccept
button.config(image = unselected,
bg=unselected_bg,
)
is_on = True
#Object detection and keystroke
def start():
while(True):
screenshot = wincap.get_screenshot()
points = vision_accept.find(screenshot, .325, 'rectangles')
if cv.waitKey(1) == ord('q'):
cv.destroyAllWindows()
break
#Button
button = Button(window,
image=unselected,
command=bttnup)
button.pack()
I have also tried:
def bttnup():
#starts autoaccept
if button['bg'] == 'unselected_bg':
threading.join(1)
button.config(image=selected,
bg=selected_bg
)
start()
return
#Stops autoaccept
if button['bg'] == 'selected_bg':
threading.start()
button.config(image = unselected,
bg=unselected_bg,
)
return
#start object detection
def start():
i = 1
while button['bg'] == 'selected_bg':
i = i+1
# get an updated image of the game
screenshot = wincap.get_screenshot()
# screenshot = cv.resize(screenshot, (800, 600))
points = vision_accept.find(screenshot, .325, 'rectangles')
sleep(5)
#Button!
button = Button(window,
image=unselected,
command=lambda: bttnup())
So I went and changed a majority of the code to use .after instead of using a while statement. Its not as clean looking, but it does what it needs to.
created a function for on, and one for off and have the button cycle when the button is fixed. when the on button is pushed it calls to the start function which then cycles every second.
# initialize the WindowCapture class
wincap = WindowCapture()
# Initialize the Vision class
vision_accept = Vision('accept.jpg')
#window parameters
window = Tk()
window.resizable(False, False)
window.title('AutoAccept')
unselected = PhotoImage(file='Button_unselected.png')
selected = PhotoImage(file='matching.png')
unselected_bg='white'
selected_bg='black'
is_on = True
def start_on():
global is_on
#starts autoaccept
button.config(image = selected,
bg=selected_bg,
command = start_off
)
start()
is_on = True
def start_off():
global is_on
button.config(image = unselected,
bg=unselected_bg,
command = start_on
)
is_on = False
#start object detection
def start():
if is_on:
# get an updated image of the game
screenshot = wincap.get_screenshot()
points = vision_accept.find(screenshot, .32, 'rectangles')
window.after(500, start)
button = Button(window,
image=unselected,
command=start_on
)
button.pack()
window.mainloop()
I just recently made a pomodoro clock implementation on python and I was facing some trouble with its execution but I came up with a solution for it but I observed a discrepancy in its execution.
So my whole code and implementation looks something like this (my code is not clean or pretty; sorry about that, I am one of those people who uses a mixture of tabs and spaces):
from tkinter import *
from time import *
from threading import Thread
# ---------------------------- CONSTANTS ------------------------------- #
PINK = "#e2979c"
RED = "#e7305b"
GREEN = "#9bdeac"
YELLOW = "#f7f5dd"
FONT_NAME = "Courier"
WORK_MIN = 25
SHORT_BREAK_MIN = 5*60000
LONG_BREAK_MIN = 20*60000
class Reset:
def __init__(self):
self.click = False
self.activation = False
def button_command(self):
self.click = True
def timer_command(self):
return self.click
# ---------------------------- TIMER RESET ------------------------------- #
timer_reset = Reset()
reset = timer_reset.click
# ---------------------------- TIMER MECHANISM ------------------------------- #
def timer_mechanism():
minute=0
if counter[0]<60000:
if counter[0] % 1000 == 0 and counter[0]/1000<10:
canvas.itemconfig(clock_counter, text=(f"00:0{int(counter[0]/1000)}"))
canvas.update_idletasks()
elif counter[0]% 1000==0 and counter[0]/1000>=10:
canvas.itemconfig(clock_counter, text=(f"00:{int(counter[0] / 1000)}"))
canvas.update_idletasks()
elif counter[0]>=60000:
if counter[0]%1000==0:
transition=int(counter[0]/1000)
while transition>=60:
minute+=1
transition-=60
second=transition
if second<10 and minute<10:
canvas.itemconfig(clock_counter,text=(f"0{minute}:0{second}"))
canvas.update_idletasks()
elif second>=10 and minute<10:
canvas.itemconfig(clock_counter,text=(f"0{minute}:{second}"))
canvas.update_idletasks()
elif minute>=10 and second<10:
canvas.itemconfig(clock_counter, text=(f"{minute}:0{second}"))
canvas.update_idletasks()
else:
canvas.itemconfig(clock_counter,text=(f"{minute}:{second}"))
canvas.update_idletasks()
# -----------------------------New countdown mechanism----------------------------#
i = [4]
def start_countdown():
if not timer_reset.click and i[0] > 0:
# keep listening to the function that receives from the button while doing an after for the 25 min and 10 min respectively.3
timer.config(text="Work", fg=GREEN)
window.update_idletasks()
# the solution to this problem is to break up the after process instead of doing it all at the same time make use of small increments so that this after could finish withing a second or two and then go to another function that contains all the timer_reset.click values update and come everytime until we reach a limit somewhere probably an array value that will be updated until we get to the 25 minute mark so this insures as the signal from the button will be accepted.
execute("Work")
# start=time()
# end=time()
# while end-start<5:
# end=time()
# timer_reset.click=timer_reset.timer_command()
else:
window.destroy()
timer_reset.click = False
i[0] = 4
beginning()
def rest():
global start_time
start_time=0
timer_reset.activation = True
if not timer_reset.click:
global restloop
restloop = True
global workloop
workloop = False
timer.config(text="Break", fg=PINK)
if i[0] == 4:
global frame
frame = Frame(window, width=20, height=20)
frame.grid(row=3, column=1)
tick = Label(frame, text="✔", font=(FONT_NAME, 12, "bold"), bg=YELLOW, fg=GREEN)
tick.pack(side=LEFT)
window.update_idletasks()
if i[0] > 0:
execute("Break")
if timer_reset.click:
timer_reset.click = False
window.destroy()
beginning()
else:
window.destroy()
beginning()
else:
window.destroy()
i[0] = 4
timer_reset.click = False
beginning()
i[0] -= 1
counter=[0]
def execute(identifier):
if identifier=="Work":
window.after(1,mirror,LONG_BREAK_MIN)
if identifier=="Break":
window.after(1,mirror,SHORT_BREAK_MIN)
def mirror(value):
if timer_reset.click:
window.destroy()
i[0]=4
timer_reset.click=False
counter[0]=0
beginning()
elif counter[0]<value:
counter[0]+=1
timer_mechanism()
if value==LONG_BREAK_MIN:
execute("Work")
if value==SHORT_BREAK_MIN:
execute("Break")
elif value==LONG_BREAK_MIN:
counter[0]=0
window.deiconify()
window.attributes("-topmost",1)
window.after(300,window.attributes,"-topmost",0)
window.update()
rest()
elif value==SHORT_BREAK_MIN:
counter[0]=0
window.deiconify()
window.attributes("-topmost",1)
window.after(300,window.attributes,"-topmost",0)
window.update()
start_countdown()
# ---------------------------- UI SETUP ------------------------------- #
def beginning():
global window
window = Tk(className="Pomodoro")
window.title("Pomodoro")
window.config(pady=50, padx=100, bg=YELLOW)
window.wm_state("normal")
global timer
timer = Label(text="Timer", font=(FONT_NAME, 50, "bold"), bg=YELLOW, foreground=GREEN)
timer.grid(row=0, column=1)
global canvas
canvas = Canvas(width=200, height=223, bg=YELLOW, highlightthickness=0)
tomato = PhotoImage(file="tomato.png")
canvas.create_image(100, 100, image=tomato)
global clock_counter
clock_counter=canvas.create_text(100, 120, text="00:00", fill="white", font=(FONT_NAME, 35, "bold"))
canvas.grid(row=1, column=1)
start = Button(text="Start", highlightthickness=0, borderwidth=0, command=start_countdown)
start.grid(row=2, column=0)
end = Button(text="Reset", highlightthickness=0, borderwidth=0, command=timer_reset.button_command)
end.grid(row=2, column=2)
window.mainloop()
beginning()
And the UI part of my code looks something like this:
So the discrepnancy I was talking about was that after starting the program and the countdown is happening if by mistake or whatever reason the user presses the Start button again the countdown starts to happen faster and if you keep pressing the start button without hitting the reset button it starts counting down even faster and faster.
Now I think I understand why this might be but this is like a sliver of what the solution might be, I might be completely wrong for all I know but is it because of the after method since every time the user hits the button he is starting another thread of the after method process and this kind of creates a sense/illusion of multiprocessing(parallelism)? I really don't know, to be honest. I might be stretching too far, but please any type of explanation would really be helpful even if it is a jab at my answer. I am very genuinely curious as to why this is happening.
Any help/suggestion will be really helpful.
I made an in game timer for Minecraft to use while speedrunning and it works great but the one problem is that when I make Minecraft fill my whole screen NOT FULLSCREEN just cover it the timer disappears.
I knew this would happen and I am wondering if this is possible to fix and make the pygame window go to the front even if it is blocked by an app that you are currently using.
You can do this with Tkinter:
This way, you can make a timer:
Always put to front
Transparent
Furthermore, tkinter is in the Standard library.
You can use this code:
from tkinter import *
from threading import Thread
from time import time
tk = Tk()
tk.title('Timer')
tk.wm_attributes('-topmost', 1) # put the window to front
pause = True
start = time()
time_when_paused = time() # time displayed when in pause
def restart(): # restart to 0
global pause, start
pause = False
start = time()
def toggle_pause():
global pause, start, time_when_paused
pause = not pause
if pause:
time_when_paused = time() # update the time displayed
else:
start += time() - time_when_paused # forget the time passed in pause
def timer():
while True:
if pause:
label['text'] = '%.2f' %(time_when_paused - start) # %.2f for 2 decimals
label['fg'] = 'orange'
else:
label['text'] = '%.2f' %(time() - start)
label['fg'] = 'green'
label = Label(tk, '', font=('Helvetica', 30), fg='orange')
label.grid(columnspan=2) # display the timer
# buttons
restart_button = Button(tk, text='Restart', width=20, command=restart)
restart_button.grid(padx=5, pady=5)
pause_button = Button(tk, text='Pause', width=20, command=toggle_pause)
pause_button.grid(padx=(0, 5), column=1, row=1)
timer_thread = Thread(target=timer)
timer_thread.start() # put in a thread because of...
tk.mainloop() # ...this which acts like "while window is not closed"
I am Trying to make a tkinter based Cube Timer Program for which I have done a console based program before I wanted to improve on it by making a GUI for it... I have made some tkinter projects before and I have a decent knowledge of how stuff works the thing is I want the Label in the tkinter window to update in every second while listening for a spacebar key on press I couldn't figure out a way to do it can someone help me?
def on_press(key):
global chk,start,end,timer_label
print('{0} pressed'.format(key))
if key == Key.space:
if chk == True:
end = time.time()
chk = False
messagebox.showinfo('Your Time',"Your Time =>"+str(round(end-start,3)))
def on_release(key):
global chk,start,end,timer_label
if key == Key.space:
if chk == False:
start = time.time()
chk = True
with Listener(on_press=on_press,on_release=on_release) as listener:
main = tk.Tk()
main.title("Cube Timer Mark 4 Prototype")
main.resizable(0,0)
tk.Label(main, text="Cube Time Mk3 Prototype", font=['Consolas',16]).grid(row = 0,column=0)
textbox = tk.Text(main,font=['Consolas',20], height = 1,width = 27)
textbox.grid(row = 1,column=0)
textbox.insert('1.0',scrambler())
timer_frame = tk.Frame(main).grid(row=2,column=0)
global timer_label
timer_label = tk.StringVar()
timer_label.set("0.0")
tk.Label(timer_frame,text = timer_label.get()+"s",font=['Consolas',20,'bold'],pady = 25).grid(row = 2,column = 0)
main.mainloop()
This is what I have tried but didn't work
You need main.after to periodically run a function like updating the label. To listen to keyboard event, you need main.bind. See example below.
Enhancement: use tk.Label(..., textvariable=timer_label) means the label will automatically update when you set timer_label
import tkinter as tk
def key_press(event):
if event.keysym == 'space':
print('pressed')
def key_release(event):
if event.keysym == 'space':
print('released')
def update_label():
# get the time from the string
time = float(timer_label.get()[:-1])
# increment the time and put it back in timer_label
timer_label.set(str(time+1) + 's')
# calling this function again 1000ms later, which will call this again 1000ms later, ...
main.after(1000, update_label)
main = tk.Tk()
main.title("Cube Timer Mark 4 Prototype")
main.resizable(0,0)
tk.Label(main, text="Cube Time Mk3 Prototype", font=['Consolas',16]).grid(row = 0,column=0)
textbox = tk.Text(main,font=['Consolas',20], height = 1,width = 27)
textbox.grid(row = 1,column=0)
#textbox.insert('1.0',scrambler())
timer_frame = tk.Frame(main).grid(row=2,column=0)
timer_label = tk.StringVar()
timer_label.set("0.0s")
# using textvariable, you can simply update timer_label and it will be reflected
tk.Label(timer_frame,textvariable = timer_label ,font=['Consolas',20,'bold'],pady = 25).grid(row = 2,column = 0)
# bind keypress and keyrelease to the functions
main.bind("<KeyPress>", key_press)
main.bind("<KeyRelease>", key_release)
# call update label function for the first time
update_label()
main.mainloop()
I've made a Python program which reads the file and then sends/receives the data towards/from the microcontroller and everything worked well until I added a menu to display short instructions.
Since the UART communication has to run in a separate thread I used threading and StringVar() to access the data from the main thread.
To demonstrate the problem I've made a short example which has nothing to do with microcontrollers.
The steps to reproduce the problem:
Click the Next screen radio button to open the second screen (at the initial screen the menu works well)
Click Help > Instructions
After (or sometimes even before) closing the message box the program will crash with:
TclStackFree: incorrect freePtr. Call out of sequence?
This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
Note:
In the original program where there are more UI elements the program always crashes before showing the message box and after removing even more UI elements the program doesn't crash every time - that's why I've left some "redundant" labels. When some more labels are added the program will crash every time.
I have narrowed the cause of the crash to:
displayVal.set()
in checkForData() function. By removing that instruction everything works well.
Moreover, after removing displayVal.trace("w", displayVal_trace) the program will not crash anymore but opening the menu will still temporarily freeze the working thread.
I thought after displayVal.set() Tkinter is trying to update the label but can't because of showing the menu - however, the problem remained even after I removed label_data = Label(up2, textvariable = displayVal).
Here is the stripped down code tested with Python 2.7:
from Tkinter import *
import tkMessageBox
import threading
import time
threadRun = True
checkDelay = 0.5
def checkForData():
global threadRun, checkDelay
print "Simulating thread for receiving messages from UART"
while threadRun == True:
print time.time()
displayVal.set(time.time()) # <-- if removed the menu works OK (no crash)
time.sleep(checkDelay)
print "No more receiving of messages"
def listenForData():
t = threading.Thread(target=checkForData)
t.daemon = False
t.start()
def stopListening():
global threadRun, checkDelay
threadRun = False
time.sleep(checkDelay + 0.1)
def exit_handler():
print "Exiting..."
stopListening()
root.destroy()
root = Tk()
right = int((root.winfo_screenwidth() - 600) / 2)
down = int(root.winfo_screenheight() / 3 - 400 / 2)
root.geometry("600x400+%d+%d" % (right, down))
root.resizable(width = False, height = False)
root.protocol("WM_DELETE_WINDOW", exit_handler)
displayVal = StringVar()
displayVal.set("nothing")
def setupView():
global masterframe
masterframe = Frame()
masterframe.pack(fill=BOTH, expand=True)
selectPort() # the 1st screen for selecting COM ports
def selectPort():
global masterframe, radioVar
# remove everything from the frame
for child in masterframe.winfo_children():
child.destroy()
radioVar = StringVar()
l1 = Label(masterframe, text = "Select...")
l1.pack(pady=(50, 20))
# this would be a list of detected COM ports
lst = ["Next screen"]
if len(lst) > 0:
for n in lst:
r1 = Radiobutton(masterframe, text=n, variable=radioVar, value=n)
r1.config(command = next_screen)
r1.pack()
def mainScreen():
global masterframe, term, status
# remove previous screen from the frame
for child in masterframe.winfo_children():
child.destroy()
up1 = Frame(masterframe)
up1.pack(side=TOP)
up2 = Frame(masterframe)
up2.pack()
terminal = Frame(masterframe)
terminal.pack()
down = Frame(masterframe)
down.pack(side=BOTTOM, fill=BOTH)
label_top = Label(up1, text="Something")
label_top.pack(pady=5)
label_data = Label(up2, textvariable = displayVal)
label_data.pack(pady=(10, 0))
term = Text(terminal, height=10, width=35, bg="white")
term.pack()
term.tag_config("red", foreground="red")
term.tag_config("blue", foreground="blue")
term.insert(END, "The file has been read\n", "red")
term.insert(END, "File contents:\n")
term.insert(END, data)
status = Label(down, text="Status...", bd=1, relief=SUNKEN, anchor=W, bg="green")
status.pack(fill=X)
displayVal.trace("w", displayVal_trace) # <-- if removed only temporary freeze but no crash
def displayVal_trace(name, index, mode):
global term
if(displayVal.get() != "NOTHING"):
term.insert(END, "\nReceived: ", "blue")
term.insert(END, displayVal.get())
term.see(END)
def next_screen():
listenForData()
mainScreen()
def stop():
stopListening()
def instructions():
tkMessageBox.showinfo("Help", "This is help")
main_menu = Menu(root)
root.config(menu = main_menu)
help_menu = Menu(main_menu)
main_menu.add_cascade(label="Help", menu=help_menu)
help_menu.add_command(label="Instructions", command=instructions)
data = [[1], [2], [3]] # this would be the data read from the file
b1 = data[0][0]
b2 = data[1][0]
b3 = data[2][0]
print "Read from file:", b1, b2, b3
setupView()
root.mainloop()