0

When I made the gui, I tried to make the GUI be able to turn the bot on and off. I managed to get it to turn on, but every time its running and I alt tab to the GUI window to turn it off, the program crashes. My best guess is that it is stuck in some loop and I don't know how to fix it. Here is my code

from pynput.keyboard import Key, Controller
import pyautogui
keyboards = Controller()
import keyboard as kb
from threading import Thread
from tkinter import *
from variables import *
import time
#here are the variables
bookss = 0
lapiss = 0
main_window = Tk()
stopp = True
#this is my attempt at a stopper/starter
def stopper():
    global stopp
    stopp = False
def start():
    global stopp
    stopp = True
#this is to keep track of how much stuff i used
def add():
    global bookss
    global lapiss
    bookss += 1
    lapiss += 3
    Label(main_window,text= bookss)
    Label(main_window,text= lapiss)
#part of the bot
def book():
    cord = pyautogui.locateCenterOnScreen('book1.png', region=(630, 530, 1290, 840))
    pyautogui.moveTo(cord, duration=.25)
    keyboards.press(Key.shift)
    pyautogui.leftClick(cord)

#part of the bot
def lapis():
    coord = pyautogui.locateCenterOnScreen('lapis_lazuli1.png', region=(630, 530, 1290, 840))
    pyautogui.moveTo(coord, duration=.25)
    keyboards.press(Key.shift)
    pyautogui.leftClick(coord, duration = .25)
#here is my actual bot loop.  i can turn it on using the starter, but i am unable to turn it off because the program keeps getting stuck in a loop
def starter():
    #this is the actual bot
    while stopp:
        time.sleep(4)
        book()
        pyautogui.moveTo(1, 1, duration=0)
        time.sleep(.05)
        lapis()
        pyautogui.moveTo(1, 1, duration=0)
        time.sleep(.05)
        keyboards.release(Key.shift)
        pyautogui.moveTo(1060, 465, duration= 0)
        pyautogui.leftClick(1060, 465, duration= 0)
        pyautogui.moveTo(702, 447, duration= 0)
        time.sleep(.05)
        keyboards.press('q')
        keyboards.release('q')
        add()
#here is my attempt at a thread that is able to turn it off. it gives the error message "AttributeError: '_tkinter.tkapp' object has no attribute 'win'"
def keybinds(main_window):
    isopen = True
    while True:
        if kb.is_pressed("p"):
            if isopen == True:
                main_window.win.withdraw()
                isopen = False
            else:
                main_window.win.deiconify()
                isopen = True
                main_window.win.focus_force()
            time.sleep(0.5)
#here is the gui
Label(main_window, text= "Enchant-a-bot 4.0").pack()

quit = Button(main_window, text= "quit", command= main_window.quit).pack()

stop = Button(main_window, text = "stop", command = stopper).pack()

start = Button(main_window,text="start", command = start).pack()
#the starter command
starter = Button(main_window, text="actual start",command=starter).pack()
#stuff for threads
keybinds_thread = Thread(target=keybinds, args=(main_window,))
keybinds_thread.Daemon(True)
keybinds_thread.start()

#gui loop
main_window.mainloop()
#my problem is that when the bot turns on, the gui loop breaks and everything stops working
Park
  • 2,446
  • 1
  • 16
  • 25

1 Answers1

1

You are correct, the code is getting stuck in a loop when starter() is called. The keybinds thread isn't solving this issue because the loop in starter and the tkinter mainloop are both still running on the same thread. There are a few approaches you could take to fix this:

Make the looped code run in its own thread

To make the loop run in its own thread, you'll need to separate the looping code from the starter method. The starter method would now create the thread that runs the bot code. For example:

def starter():
    #bot code here

would become:

def runBot():
    #bot code here
def starter():
    bot_thread = Thread(target=runBot)
    bot_thread.start()

Update tkinter during your loop

To update tkinter, you just need to insert main_window.update() somewhere in your loop.

def starter():
    while stopp:
        #Your code

would become:

def starter():
    while stopp:
        #Your code
        main_window.update()

With the threaded approach, You might run into issues if a race condition exists in your program, This answer does a good job explaining race conditions, and why they're an issue. You'll want to use a threading.Lock object whenever you access a variable that's used in multiple threads, ie:

from threading import Lock
lock = Lock()
#code that accesses a variable
with lock:
    #do variable stuff here
#continue on with code

With the update method, be aware that this doesn't prevent the issue from cropping up again, it's just manually calling an iteration of the tkinter loop, and has to wait for any code called between update() calls before it will update, if that code has a delay, it will result in the gui being unresponsive.


The error message AttributeError: '_tkinter.tkapp' object has no attribute 'win' is being caused because withdraw, and other similar methods are part of a Tkinter object, so where you have main_window.win.withdraw(), you would just need main_window.withdraw()

Darorad
  • 11
  • 2